spring整合shiro(含MD5加密)

shiro简介:

shiro是apache提供的一个强大易用的Java安全框架,用于身份验证、授权、密码学和会话管理。下载源码。

开发环境及技术:

1、mysql - 5.7.21
2、navicat(mysql客户端管理工具)
3、eclipse
4、jdk9
5、tomcat 8.5
6、spring & springmvc
7、mybatis 3
8、shiro
9、maven

现在直接开始建项目,从项目中具体讲解shiro的使用,项目虽简单,却五脏俱全。


欢迎大家关注我的公众号 javawebkf,目前正在慢慢地将简书文章搬到公众号,以后简书和公众号文章将同步更新,且简书上的付费文章在公众号上将免费。


一、数据库设计:

数据库有三张表,分别是tb_user用户表,tb_role角色表,tb_permission权限表。

1、tb_user

图片发自简书App

设置外键rid关联tb_role表


图片发自简书App

2、tb_role

图片发自简书App

3、tb_permission

图片发自简书App

设置外键关联tb_role表


图片发自简书App

二、项目环境搭建:

1、新建maven web app,结构如下:

图片发自简书App

2、接下来就是添加依赖:

<dependencies>

    <!-- junit测试 -->
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
        <scope>test</scope>
    </dependency>

    <!-- 添加Servlet支持 -->
    <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>javax.servlet-api</artifactId>
        <version>3.1.0</version>
    </dependency>
    <dependency>
        <groupId>javax.servlet.jsp</groupId>
        <artifactId>javax.servlet.jsp-api</artifactId>
        <version>2.3.1</version>
    </dependency>

    <!-- 添加jtl支持 -->
    <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>jstl</artifactId>
        <version>1.2</version>
    </dependency>

    <!-- 添加Spring支持 -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-core</artifactId>
        <version>4.1.7.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-beans</artifactId>
        <version>4.1.7.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-tx</artifactId>
        <version>4.1.7.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>4.1.7.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context-support</artifactId>
        <version>4.1.7.RELEASE</version>
    </dependency>

    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-web</artifactId>
        <version>4.1.7.RELEASE</version>
    </dependency>

    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-webmvc</artifactId>
        <version>4.1.7.RELEASE</version>
    </dependency>

    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-aop</artifactId>
        <version>4.1.7.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-aspects</artifactId>
        <version>4.1.7.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-jdbc</artifactId>
        <version>4.1.7.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-test</artifactId>
        <version>4.3.14.RELEASE</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>commons-collections</groupId>
        <artifactId>commons-collections</artifactId>
        <version>3.2</version>
    </dependency>

    <!-- mybatis依赖 -->
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis-spring</artifactId>
        <version>1.2.3</version>
    </dependency>
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis</artifactId>
        <version>3.3.0</version>
    </dependency>


    <!-- 添加日志支持 -->
    <dependency>
        <groupId>log4j</groupId>
        <artifactId>log4j</artifactId>
        <version>1.2.17</version>
    </dependency>
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-log4j12</artifactId>
        <version>1.7.12</version>
    </dependency>
    
    <!-- 数据库驱动 -->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>5.1.37</version>
    </dependency>
    
    <!-- c3p0数据源 -->
    <dependency>
        <groupId>com.mchange</groupId>
        <artifactId>c3p0</artifactId>
        <version>0.9.5.2</version>
    </dependency>


    <!-- shiro相关 -->
    <dependency>
        <groupId>org.apache.shiro</groupId>
        <artifactId>shiro-core</artifactId>
        <version>1.2.4</version>
    </dependency>
    <dependency>
        <groupId>org.apache.shiro</groupId>
        <artifactId>shiro-web</artifactId>
        <version>1.2.4</version>
    </dependency>
    <dependency>
        <groupId>org.apache.shiro</groupId>
        <artifactId>shiro-spring</artifactId>
        <version>1.2.4</version>
    </dependency>

</dependencies>

依赖对应的作用在代码中有简要的注释。

3、完成配置文件:

在spring文件夹下有:
spring-dao.xml,
spring-mvc.xml,
spring-service.xml,
spring-shiro.xml

①spring-dao.xml

<!-- 配置整合mybatis过程 -->
 <!-- 1、配置数据库相关参数properties的属性:${url} -->
 <context:property-placeholder
     location="classpath:jdbc.properties" />

 <!-- 2、配置数据库连接池 -->
 <bean id="dataSource"
     class="com.mchange.v2.c3p0.ComboPooledDataSource">
     <!-- 配置连接池属性 -->
     <property name="driverClass" value="${jdbc.driver}" />
     <property name="jdbcUrl" value="${jdbc.url}" />
     <property name="user" value="${jdbc.username}" />
     <property name="password" value="${jdbc.password}" />

     <!-- c3p0连接池的私有属性 -->
     <property name="maxPoolSize" value="30" />
     <property name="minPoolSize" value="10" />
     <!-- 关闭连接不自动commit -->
     <property name="autoCommitOnClose" value="false" />
     <!-- 获取连接超时时间 -->
     <property name="checkoutTimeout" value="10000" />
     <!-- 当获取连接失败时重试次数 -->
     <property name="acquireRetryAttempts" value="2" />
 </bean>
 
 
 <!-- 3、配置mybatis的sqlSessionFactory -->
 <bean id="sqlSessionFactory"
     class="org.mybatis.spring.SqlSessionFactoryBean">
     <property name="dataSource" ref="dataSource" />
     <!-- 自动扫描mappers.xml文件 -->
     <property name="mapperLocations"
         value="classpath:mappers/*.xml" />
     <!-- mybatis配置文件 -->
     <property name="configLocation"
         value="classpath:mybatis-config.xml" />
 </bean>

 <!-- 4、DAO接口所在包名,Spring会自动查找其下的类 -->
 <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
     <property name="basePackage" value="com.zhu.shiroweb.dao" />
     <property name="sqlSessionFactoryBeanName"
         value="sqlSessionFactory"></property>
 </bean>

②spring-service.xml

    <!-- 自动扫描 -->
    <context:component-scan base-package="com.zhu.shiroweb.service" />

③spring-mvc.xml

 <!-- 配置springmvc -->
    <!-- 1、开启springMvc注解模式 -->
    <mvc:annotation-driven />
    
    <!-- 2、视图解析器 -->
    <bean id="viewResolver"
        class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/" />
        <property name="suffix" value=".jsp"></property>
    </bean>
        
    <!-- 3、开启Shiro注解 -->
    <bean
        class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"
        depends-on="lifecycleBeanPostProcessor" />
    <bean
        class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
        <property name="securityManager" ref="securityManager" />
    </bean>
                 
    <!-- 4、扫描web相关的bean -->
    <context:component-scan base-package="com.zhu.shiroweb.controller"/>

注意:开启shiro注解的配置要写在spring-mvc.xml中,具体原因我也不太清楚,有知道的大佬还请留言指教哦!

④spring-shiro.xml

<!-- 1、将自定义Realm加入IOC容器 -->
    <bean id="myRealm" class="com.zhu.shiroweb.realm.MyRealm">
        
        <!-- 配置MD5加密,若不进行MD5加密,这段代码不用 -->
        <property name="credentialsMatcher">
            <bean class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
                <!-- MD5加密 -->
                <property name="hashAlgorithmName" value="MD5"/>
                <!-- 加密次数 -->
                <property name="hashIterations" value="1024"/>
            </bean>
        </property> 
        <!-- 配置MD5加密,若不进行MD5加密,这段代码不用 -->
        
    </bean>  
    
    <!-- 2、配置安全管理器 -->
    <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">  
      <property name="realm" ref="myRealm"/>  
    </bean>  
    
    <!-- 3、配置Shiro过滤器,id名必须和web.xml中的过滤器名一致 -->
    <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">  
        <!-- Shiro的核心安全接口,这个属性是必须的 -->  
        <property name="securityManager" ref="securityManager"/>
        <!-- 身份认证失败,则跳转到登录页面的配置 -->  
        <property name="loginUrl" value="/login.jsp"/>
        <!-- 权限认证失败,则跳转到指定页面 -->  
        <property name="unauthorizedUrl" value="/unauthor.jsp"/>  
        <!-- Shiro连接约束配置,即过滤链的定义 -->  
        <property name="filterChainDefinitions">  
            <value>  
                /login=anon
                /admin/**=authc,roles[admin]
            </value>  
        </property>
    </bean>  
    
    <!-- 4、保证实现了Shiro内部lifecycle函数的bean执行 -->  
    <bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>

以下配置文件在resources根目录:
⑤jdbc.properties

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql:///#?useUnicode=true&characterEncoding=utf8
jdbc.username=#
jdbc.password=#

⑥log4j.properties

log4j.rootLogger=DEBUG, Console  
  
#Console  
log4j.appender.Console=org.apache.log4j.ConsoleAppender  
log4j.appender.Console.layout=org.apache.log4j.PatternLayout  
log4j.appender.Console.layout.ConversionPattern=%d [%t] %-5p [%c] - %m%n  
  
log4j.logger.java.sql.ResultSet=INFO  
log4j.logger.org.apache=INFO  
log4j.logger.java.sql.Connection=DEBUG  
log4j.logger.java.sql.Statement=DEBUG  
log4j.logger.java.sql.PreparedStatement=DEBUG  

⑦mybatis-config.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <!-- 别名 -->
    <typeAliases>
        <package name="com.zhu.shiroweb.entity"/>
    </typeAliases>
</configuration>

web.xml在WEB-INF目录下:
⑧web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
                      http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
    version="3.1" metadata-complete="true">
    <display-name>shiroweb</display-name>
    <welcome-file-list>
        <welcome-file>index.jsp</welcome-file>
    </welcome-file-list>

    <!-- Spring配置文件 -->
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:spring/spring-*.xml</param-value>
    </context-param>
    
    <!-- 添加对springmvc的支持 -->
    <servlet>
        <servlet-name>springMVC</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:spring/spring-mvc.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
        <async-supported>true</async-supported>
    </servlet>
    <servlet-mapping>
        <servlet-name>springMVC</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
    
    <!-- Spring监听器 -->
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
    
    
    <!-- shiro过滤器定义 -->
    <filter>
        <filter-name>shiroFilter</filter-name>
        <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
        <init-param>
            <!-- 该值缺省为false,表示生命周期由SpringApplicationContext管理,设置为true则表示由ServletContainer管理 -->
            <param-name>targetFilterLifecycle</param-name>
            <param-value>true</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>shiroFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
    
    <!-- 编码过滤器,非必须 -->
    <filter>
        <filter-name>encodingFilter</filter-name>
        <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
        <async-supported>true</async-supported>
        <init-param>
            <param-name>encoding</param-name>
            <param-value>UTF-8</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>encodingFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

</web-app>

至此已完成所有配置,所有的配置文件核心代码都写了注释。

三、项目功能实现

登录验证:

项目功能描述:
在数据库中有两个用户,一个tom,角色为admin,对应的权限有create,delete,query和update,另一个用户cat,角色为guest,权限只有create和query。

1、首先新建User实体类(set、get方法略):
User.java

public class User {
    private Integer uid;
    private String userName;
    private String password;
}

2、dao层的开发
UserDao.java

public interface UserDao {
    
    /**
     * 根据用户名查询用户
     * @param userName
     * @return
     */
    public User getByUserName(String userName);
    
    /**
     * 根据用户名查询角色
     * @param userName
     * @return
     */
    public Set<String> getRoles(String userName);
    
    /**
     * 根据用户名查询权限
     * @param userName
     * @return
     */
    public Set<String> getPermissions(String userName);

}

UserDao.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.zhu.shiroweb.dao.UserDao">

    <resultMap type="com.zhu.shiroweb.entity.User" id="UserResult">
        <result property="uid" column="uid"/>
        <result property="userName" column="user_name"/>
        <result property="password" column="pass_word"/>
    </resultMap>
    
    <select id="getByUserName" parameterType="String" resultMap="UserResult">
        select * 
        from tb_user 
        where user_name=#{userName}
    </select>
    
    <select id="getRoles" parameterType="String" resultType="String">
        select r.role_name
        from tb_user u,tb_role r 
        where u.rid=r.rid 
        and u.user_name=#{userName}
    </select>
    
    <select id="getPermissions" parameterType="String" resultType="String">
        select p.permission_name 
        from tb_user u,tb_role r,tb_permission p 
        where u.rid=r.rid 
        and p.rid=r.rid 
        and u.user_name=#{userName}
    </select>

</mapper> 

注意:这里并没有role和permission对应的实体类,也可以新建其对应的实体类,然后把他们设置为User的成员变量,用List集合装载。但是这样做更麻烦一点,因为等下shiro要用到的就是Set集合,如果是List等下还需做转换。

3、dao层测试
写到这可以做一下junit测试,由于篇幅原因,此处不再赘述。

4、service层开发
由于并没有增加逻辑,只是简单调用dao层,所以不再说明。

5、自定义realm
MyRealm.java

public class MyRealm extends AuthorizingRealm {

    @Autowired
    private UserService userService;

    /**
     * 为登录用户授予权限和角色
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        String userName = (String) principals.getPrimaryPrincipal();
        SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
        authorizationInfo.setRoles(userService.getRoles(userName));
        authorizationInfo.setStringPermissions(userService.getPermissions(userName));
        return authorizationInfo;
    }

    /**
     * 验证当前登录用户
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        String userName = (String) token.getPrincipal();
        User user = userService.getByUserName(userName);
        
        //使用md5加密
        //当前realm对象的name
        String realmName = getName();
        //盐值
        ByteSource credenttialsSalt = ByteSource.Util.bytes(user.getUserName());
        //封装用户信息,构建AuthenticationInfo对象并返回
        AuthenticationInfo authcInfo = new SimpleAuthenticationInfo(user.getUserName(), user.getPassword(),
                credenttialsSalt, realmName);
        return authcInfo;
        //使用md5加密
        

        //不加密
        /*
         * if (user != null) { 
         *     AuthenticationInfo authcInfo = new
         *            SimpleAuthenticationInfo(user.getUserName(), user.getPassword(), "xx");
         *      return authcInfo; 
         * } else { 
         *      return null;
         * }
         */
        //不加密
    }

这个类就验证登录和为用户授权两个方法,代码中已有注释说明。若不使用MD5加密,则把我写了MD5加密注释之间那段代码注释掉,把下面注释放开就行;若要使用MD5加密,数据库中的密码也得是加密后的密码可以通过下面的方法获取明文密码对应的密文密码。
获取密文的方法:

public static void main(String[] args) {
        String hashAlgorithName = "MD5";
        String password = "5678";
        int hashIterations = 1024;
        ByteSource credentialsSalt = ByteSource.Util.bytes("cat");
        Object obj = new SimpleHash(hashAlgorithName, password, credentialsSalt, hashIterations);
        System.out.println(obj);
    }

6、用户登录的Controller

@Controller
@RequestMapping("/user")
public class UserController {
    
    /**
     *
     * @param user
     * @param request
     * @return
     */
    @RequestMapping("/login")
    public String login(User user,HttpServletRequest request){
        //获取当前登录用户
        Subject subject=SecurityUtils.getSubject();
        //封装表单中提交的用户名和密码
        UsernamePasswordToken token=new UsernamePasswordToken(user.getUserName(), user.getPassword());
        try{
            //调用login方法,传入封装好的token
            subject.login(token);
            //登录成功跳转success.jsp
            return "redirect:/success.jsp";
        }catch(Exception e){
            e.printStackTrace();
            //登录失败就重新登录
            request.setAttribute("errorMsg", "登录失败");
            return "login";
        }
    }
    
}

7、login.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<form action="${pageContext.request.contextPath }/user/login" method="post">
    userName:<input type="text" name="userName" value="${user.userName }"/><br/>
    password:<input type="password" name="password" value="${user.password }"><br/>
    <input type="submit" value="login"/><font color="red">${errorMsg }</font>
</form>
</body>
</html>

过程梳理:
在controller中获取到前端表单输入的用户名和密码,封装到token中,然后调用subject的login的方法,传入这个token,这个token就会把数据带到realm中,realm中再调service层查询,根据前端token中携带的用户名去查询数据库中记录,进行密码比对。

这里解释spring-shiro.xml中/login=anon的作用,这行代码的意思就是不拦截登录方法,可以匿名访问。

以上就是验证登录的整个流程。

授权:

需求描述:
1、指定角色:
AdminController只有具有admin角色(tom)才能访问;
GuestController只有具有guest角色(cat)才能访问;
2、指定权限:
PermissionController只有具有create权限(tom和cat)的用户才能访问;
涉及知识点:
1、在spring-shiro.xml中进行授权验证
2、注解方式授权验证
3、jsp中授权验证
4、多级路由匹配规则

正式开始:

1、AdminController.java

@Controller
@RequestMapping("/admin")
public class AdminController {

    /**
     *
     * @param user
     * @param request
     * @return
     */
    @RequestMapping("/test")
    public String adminTest() {
        //Subject subject = SecurityUtils.getSubject();
        //if(subject.hasRole("admin")) {
            return "admin";
        //}else {
        //  return "login";
        //}
        
    }

}

这个授权验证写在spring-shiro.xml中:/admin/**=authc,roles[admin],这样就表示要有admin这个角色的用户才能访问。同时这个也是双重路由,/admin/**就表示拦截admin开头的路由。

2、GuestController.java

public class GuestController {
    
    @RequiresRoles("guest")
    @RequestMapping("/guest")
    public String guestTest() {
        return "guest";
    }

}

这个是使用注解方式进行权限验证的,@RequiresRoles("guest")就表示要有guest这个角色才能访问这个路由。

3、PermissionsController.java

@Controller
public class PermissionsController {
    /**
     *
     * @param user
     * @param request
     * @return
     */
    @RequestMapping("/permissions")
    public String permissionTest(){
        Subject subject = SecurityUtils.getSubject();
        if(subject.isPermitted("create")) {
            return "permission";
        }else {
            return "login";
        }
        
    }
    
}

这个是直接在Controller中获取到登录用户,然后用Subject对象的isPermitted方法就行判断。subject.isPermitted("create")用来判断登录的用户是否有create权限,有就true,没有就是false。

4、接下来看看前端页面:
①success.jsp
登录成功跳转到这个页面

<%@ taglib prefix="shiro" uri="http://shiro.apache.org/tags"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
       欢迎你!
    <br>
    <a href="http://localhost:8080/shiroweb/admin/test">有admin角色才能访问</a>
    <br>

    <a href="http://localhost:8080/shiroweb/permissions">有create权限就可以访问</a><br>
    <a href="http://localhost:8080/shiroweb/guest">guest才能访问</a>

</body>
</html>

这里定义了三个链接,分别指向上面那三个controller。

②admin.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix="shiro" uri="http://shiro.apache.org/tags" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
  欢迎有admin角色的你!
</body>
</html>

③guest.jsp

    pageEncoding="utf-8"%>
    <%@ taglib prefix="shiro" uri="http://shiro.apache.org/tags" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Insert title here</title>
</head>
<body>

   <shiro:hasRole name="guest">
        欢迎guest!(只有guest角色登录进来才能显示这段话)<shiro:principal/>
    </shiro:hasRole>
    
    <shiro:hasRole name="admin">
        欢迎admin!(只有admin角色登录进来才能显示这段话)<shiro:principal/>
    </shiro:hasRole>

</body>
</html>

这里就用到了jsp标签形式判断角色,<shiro:hasRole name="#">xxx</shiro:hasRole>就是用来判断当前用户有没有#角色,有#角色才会显示xxx内容。

注意:使用shiro的jsp标签要在jsp页面中添加<%@taglibprefix="shiro"uri="http://shiro.apache.org/tags"%>

④permission.jsp

    pageEncoding="UTF-8"%>
<%@ taglib prefix="shiro" uri="http://shiro.apache.org/tags" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
  欢迎有你! <shiro:hasPermission name="delete">
     只有拥有delete权限的人登录进来才能显示这段话.<shiro:principal/>
    </shiro:hasPermission>
</body>
</html>

这里用到了<shiro:hasPermissionname="#">xxx</shiro:hasPermission>来判断权限,只有具有#权限的用户才会显示xxx内容。

⑤unauthor.jsp

    pageEncoding="utf-8"%>
    <%@ taglib prefix="shiro" uri="http://shiro.apache.org/tags" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Insert title here</title>
</head>
<body>
  对不起,你没有操作权限!
  <a href="login.jsp">点此登录</a>
</body>
</html>

权限认证失败时跳转到此页面,因为在spring-shiro.xml中配置过。

四、项目测试

1、用tom登录
分析:tom具有admin角色和所有权限,所以success.jsp页面的前两个链接可以访问,且permission.jsp的<shiro:hasPermission name="delete">标签里面的话会显示。第三个链接不能访问,会抛出异常,因为需要guest角色才能访问。
结果:

图片发自简书App

图片发自简书App
图片发自简书App
图片发自简书App
图片发自简书App

2、用cat登录
分析:cat具有guest角色和create 以及query权限,所以success.jsp的第一个链接不能访问,会跳转到unauthor.jsp,第二个链接可以访问,但是不能显示<shiro:hasPermission name="delete">标签里面的话,第三个链接可以访问,且能显示<shiro:hasRole name="guest">里面的话。
结果:

图片发自简书App
图片发自简书App
图片发自简书App

所有结果符合预期,测试通过!

五、知识点补充

1、url匹配规则:

/admin?=authc

表示admin1需要认证,admin2也需要,但是admin不一定,因为问号表示单个字符

/admin*=authc

表示admin1需要认证,admin21需要,admin也需要,*号表示一个或多个字符

/admin/**=authc

可以匹配多路径,比如admin/a/b

2、
Subject对象除了isPermitted("#")判断是否拥有#权限,还有hasRole("#")判断是否有#角色,hasRoles(Arrays.asList("role1","role2"))来判断是否有role1和role2角色。

3、其他注释:

@RequiresAuthentication
表示要验证通过才能被访问

@RequiresGuest
表示之前session中没有被验证过才能访问

@RequiresPermissions("create")
表示有create权限才能访问

@RequiresRoles("admin")
只有admin这个角色才能访问

@RequiresUser
指定用户才能访问

总结:

shiro提供了认证登录,授权,密码加密等功能,方便易用。授权可以在spring-shiro中定义过滤链,可以使用注释,也可以在controller 方法中用Subject对象的方法判断,还可以在jsp中使用标签。

以上内容是个人学习笔记整理,如有错误,欢迎批评指正!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容