• Shiro学习与笔记


    Shiro

    Shiro理论

    什么是shiro

    Shiro是一个强大易用的java安全,提供了认证、授权、加密、会话管理、与web集成、缓存等功能,对于任何一个应用程序,都可以提供全面的安全服务,相比其他安全框架,shiro要简单的多。

    功能

    • 身份验证(核心功能);
    • 资源授权(核心功能);
    • 密码加密(非核心功能);
    • 会话管理(非核心功能);
    • Remember Me(非核心功能);

    组件

    • Subject:应用层和shiro框架交互的对象
    • Realm:实现身份验证和资源授权的核心组件:域,shiro从Realm获取安全数据(如用户、角色、权限),就是说SecurityManager要验证用户身份,那么它需要从Realm获取相应的用户进行比较以确定用户身份是否合法;也需要从Realm得到用户相应的角色/权限进行验证用户是否能进行操作;可以把Realm看成DataSource,即安全数据源。
    • RememberMeManager:安全管理器;即所有与安全有关的操作都会与SecurityManager交互;且它管理着所有Subject;可以看出它是shiro的核心, SecurityManager相当于spring mvc中的dispatcherServlet前端控制器。
      • SimpleCookie:记住我——Cookie
      • CookieRememberMeManager:记住我—— Cookie 管理器;
    • SessionManager
      • SimpleCookie:会话管理—— Cookie
      • DefaultWebSessionManager:会话配置;
    • SecurityManager:安全管理器,它负责与 Shiro 的其他组件进行交互,需要注入 RealmRememberMeManagerSessionManager 等组件,且管理着所有的 Subject;
    • ShiroFilterFactoryBeanShiro 过滤器工厂,注入 SecurityManager、定义过滤器、指定路径拦截规则等;
    • ShiroDialect:Shiro 方言,支持 Thymeleaf 页面 Shiro 标签;
    • DefaultAdvisorAutoProxyCreator:Advisor 代理类生成器;
    • AuthorizationAttributeSourceAdvisor:创建 Advisor 代理类,扫描 Shiro 注解;
    • 应用代码通过 Subject 来进行认证和授权,Subject 委托给 SecurityManager,我们需要给 Shiro 的 SecurityManager 注入 Realm,从而让 SecurityManager 能对用户及其权限进行判断;

    img

    shiro注解

    • @RequiresAuthentication : 表示当前 Subject 需要登录;
    • @RequiresUser : 表示当前 Subject 需要登录或记住我;
    • @RequiresGuest : 表示当前 Subject 是游客身份;
    • @RequiresRoles(value={“admin”, “user”}, logical=Logical.AND):当前 Subject 需要的角色;
    • @RequiresPermissions (value={““,””}, logical= Logical.OR) :当前 Subject 需要拥有的资源;

    既可以用在controller中,也可以用在service中建议将shiro注解放入controller,因为如果service层使用了spring的事物注解,那么shiro注解将无效

    shiro的优点

    1、 简单的身份验证,支持多种数据源
    2、对角色的简单授权,支持细粒度的授权(方法)
    3、支持一级缓存,以提升应用程序的性能
    4、内置基于POJO的企业会话管理,适用于web及非web环境
    5、非常简单的API加密
    6、不跟任何框架绑定,可以独立运行
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    运行流程

    1. 首先调用 Subject.login(token) 进行登录,其会自动委托给 Security Manager,调用之前必须通过 SecurityUtils.setSecurityManager() 设置;
    2. SecurityManager 负责真正的身份验证逻辑;它会委托给 Authenticator 进行身份验证;
    3. Authenticator 才是真正的身份验证者,Shiro API 中核心的身份认证入口点,此处可以自定义插入自己的实现;
    4. Authenticator 可能会委托给相应的 AuthenticationStrategy 进行多 Realm 身份验证,默认 ModularRealmAuthenticator 会调用 AuthenticationStrategy 进行多 Realm 身份验证;
    5. Authenticator 会把相应的 token 传入 Realm,从 Realm 获取身份验证信息,如果没有返回 则抛出异常表示身份验证失败了。此处可以配置多个 Realm,将按照相应的顺序及策略进行访问。

    前端标签

    jsp页面
    首先要导入标签库;
    <%@ taglib uri="http://shiro.apache.org/tags" prefix="shiro" %>:导入标签库;
        
    :游客访问;
        
    :用户需要登录或记住我;
        
    :用户需要登录;
        
    :用户需要拥有某种角色访问;
        
    :用户需要拥有某些角色;
        
    :用户需要拥有某资源;进行显示
        
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    Thymeleaf
    导入
    <html 
           xmlns:shiro="http://www.pollix.at/thymeleaf/shiro"
          >
        
    html>
    
    shiro:guest:游客访问;
    shiro:user:用户需要登录或记住我;
    shiro:authenticated:用户需要登录;
    shiro:hasRole:用户需要某角色;
    shiro:hasAnyRoles:用户需要某些角色;
    shiro:hasPermission:用户需要某权限;
    -----------------------------------------------------------
    <li shiro:hasAnyRoles="admin,manager">:用户需要 admin 或 manager 角色可访问;
        
    <li><span shiro:principal/>li>:身份验证器包装的用户名时,页面获取用户名;
    
    <span shiro:principal property="loginName"/>:身份验证器中包装 user 对象时,页面获取用户名;
    
    <input type="hidden" id="userId" th:value="${session.user.userId}">:页面获取 session 中 user 对象;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    在spring框架中集成shiro

    Pom文件进行依赖配置

    可以在maven仓库包https://mvnrepository.com/,根据三要素进行查找

    
    <properties>
        <shiro.version>1.7.1shiro.version>
    properties>
    
        <dependency>
          <groupId>org.apache.shirogroupId>
          <artifactId>shiro-coreartifactId>
          <version>${shiro.version}version>
        dependency>
    
    
        <dependency>
          <groupId>org.apache.shirogroupId>
          <artifactId>shiro-springartifactId>
          <version>${shiro.version}version>
        dependency>
    
    
    <dependency>
      <groupId>org.apache.shirogroupId>
       <artifactId>shiro-webartifactId>
       <version>${shiro.version}version>
    dependency>
    
    
    <dependency>
      <groupId>org.apache.shirogroupId>
       <artifactId>shiro-aspectjartifactId>
       <version>${shiro.version}version>
    dependency>
    
    
    <dependency>
      <groupId>org.apache.shirogroupId>
       <artifactId>shiro-ehcacheartifactId>
       <version>${shiro.version}version>
    dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38

    项目跑起来了,只是控制台输出了

    SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
    SLF4J: Defaulting to no-operation (NOP) logger implementation
    SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.
    
    • 1
    • 2
    • 3

    需要配置日志包

    shiro使用slf4j作为日志框架,所以必需配置slf4j。同时,使用log4j作为底层的日志实现框架。

    
      org.slf4j
      slf4j-api
      1.7.25
    
    
      org.slf4j
      slf4j-log4j12
      1.7.25
    
    
      log4j
      log4j
      1.2.17
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    集成Shiro

    在Spring框架中集成Shiro,本质上是与Spring IoC容器和Spring MVC框架集成.

    web.xml中的设置
      
      <filter>
        <filter-name>shiroFilterfilter-name>
        <filter-class>org.springframework.web.filter.DelegatingFilterProxyfilter-class>
        
        
        <init-param>
          <param-name>targetFilterLifecycleparam-name>
          <param-value>trueparam-value>
        init-param>
      filter>
      <filter-mapping>
        <filter-name>shiroFilterfilter-name>
        <url-pattern>/*url-pattern>
      filter-mapping>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    shiro的bean配置文件,

    其中包含过滤设置。此文件名为spring-shiro.xml,里面定义了需要的Bean,完成诸多功能。

    
    
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:context="http://www.springframework.org/schema/context"
           xsi:schemaLocation="
           http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans.xsd
           http://www.springframework.org/schema/context
    	   http://www.springframework.org/schema/context/spring-context-4.3.xsd
    
    ">   
        
        <context:component-scan base-package="com.qq"/>
        
         
        <bean id="userRealm" class="com.qq.realm.UserRealm"/>
        
        
        <bean id="advisorAutoProxyCreator" class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" depends-on="lifecycleBeanPostProcessor">
            
            <property name="proxyTargetClass" value="true"/>
        bean>
        
        
        <bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor" />
    
        
        
         
        <bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
            <property name="securityManager" ref="securityManager"/> 
        bean>
        
        
           <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
    
            <property name="realm" ref="userRealm"/>
        bean>
        
        
         
        
        <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
    
            
          <property name="securityManager" ref="securityManager"/>
    
          
            
            <property name="loginUrl" value="/login/view"/>
    
              
            <property name="unauthorizedUrl" value="/login/view" />
    
            
            
            
            
            <property name="successUrl" value="/home"/>
    
            
    
            
            
            <property name="filterChainDefinitions" >
                <value>
                    /login/view = anon
                    /back/student/zzz =authc,roles[admin]
                    /back/** = authc
                value>
            property>
    
    
       
    beans>
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91

    或者:springShiro.xml

    
    
    
    <beans xmlns="http://www.springframework.org/schema/beans" 
    
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    
        xmlns:context="http://www.springframework.org/schema/context" 
    
        xsi:schemaLocation="http://www.springframework.org/schema/beans 
    
                http://www.springframework.org/schema/beans/spring-beans-4.0.xsd 
    
                http://www.springframework.org/schema/context  
    
                http://www.springframework.org/schema/context/spring-context-4.0.xsd"
    
        default-lazy-init="true">
    
        
    
        
    
        <bean id="myRealm" class="com.sfac.springMvc.config.MyRealm" />
    
        
    
        
    
        <bean id="rememberMeCookie" class="org.apache.shiro.web.servlet.SimpleCookie">
    
            <property name="name" value="rememberMe"/>
    
            <property name="maxAge" value="604800"/>
    
            <property name="httpOnly" value="true"/>
    
        bean>
    
        <bean id="rememberMeManager" class="org.apache.shiro.web.mgt.CookieRememberMeManager">
    
            <property name="cookie" ref="rememberMeCookie"/>
    
        bean>
    
        
    
        
    
        <bean id="sessionManager" class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager">
    
            
    
            <property name="globalSessionTimeout" value="6000000"/>
    
            
    
            <property name="deleteInvalidSessions" value="true"/>
    
            
    
            <property name="sessionIdUrlRewritingEnabled" value="false" />
    
        bean>
    
        
    
        
    
        <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">  
    
            <property name="realm" ref="myRealm"/>
    
            <property name="rememberMeManager" ref="rememberMeManager">property>
    
            <property name="sessionManager" ref="sessionManager" />
    
        bean>
    
        
    
        
    
        <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
    
            <property name="securityManager" ref="securityManager"/>
    
            <property name="loginUrl" value="/login"/>
    
            <property name="successUrl" value="/common/dashboard"/>
    
            
    
            <property name="filterChainDefinitions">  
    
                <value>  
    
                    /static/**=anon
    
                    /register=anon
    
                    /login=anon
    
                    /forgot=anon
    
                    /logout=logout
    
                    /api/**=anon
    
                    /**=authc
    
                value>  
    
            property>  
    
        bean>
    
        
    
        
    
        
    
        
    
        
    
        <bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>
    
        
    
        <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"
    
            depends-on="lifecycleBeanPostProcessor">
    
            <property name="proxyTargetClass" value="true" />
    
        bean>
    
        
    
        <bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
    
            <property name="securityManager" ref="securityManager" />
    
        bean>
    
    beans>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166

    必须是在MVC配置文件中用

    
    
    • 1

    将spring-shiro.xml载入。

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dBZwMJxH-1662297213062)(C:\Users\飞\AppData\Roaming\Typora\typora-user-images\image-20220902220333286.png)]

    例子:

    • 身份验证

      UserRealm

      package com.qq.realm;
      import com.qq.model.User;
      import com.qq.service.UserService;
      import org.apache.shiro.authc.*;
      import org.apache.shiro.authz.AuthorizationInfo;
      import org.apache.shiro.authz.SimpleAuthorizationInfo;
      import org.apache.shiro.realm.AuthorizingRealm;
      import org.apache.shiro.subject.PrincipalCollection;
      import org.springframework.beans.factory.annotation.Autowired;
      
      /**
       * @description:com.qq.realm_studyssm
       * @author: 霏宇
       * @time: 2022/8/5,9:55
       */
      public class UserRealm extends AuthorizingRealm {
          @Autowired
          UserService userService;
          //授权:
          @Override
          protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
              SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
      
              String name =(String)principalCollection.getPrimaryPrincipal();
              if (name.equals("admin")){
                  info.addStringPermission("admin");
              }else if (name.equals("lisi")){
                  info.addStringPermission("worker");
                  info.addRole("admin");
              }
              return info;
      
          }
          /**
           * 认证
           * @param token
           * @return
           * @throws AuthenticationException
           */
      
          @Override
          protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
            //一旦此用户不存在就主动抛出异常
              UsernamePasswordToken upToken = (UsernamePasswordToken)token;
               String name =upToken.getUsername();
               String password=new String(upToken.getPassword());
      
               User user =userService.selectByName(name);
               if(null==user){
                   throw new UnknownAccountException("你输入的账户不存在!");
               }else {
                   User user2 =userService.selectByNamePassword(name,password);
                   if(null==user2){
                       throw new IncorrectCredentialsException("密码错误");
                   }else {
                       return  new SimpleAuthenticationInfo(token.getPrincipal(),token.getCredentials(),this.getName());
                   }
               }
      
      //         if(!name.equals("list")&&!name.equals("admin")){
      //             throw new UnknownAccountException("你输入的账户不存在!");
      //         }else {
      //             //这里假设系统的用户只有两个 (zhangsan,123456)、(lisi,888999)
      //             if(name.equals("list")&&password.equals("789") ||name.equals("admin")&&password.equals("123456")){
      //
      //                 return   new SimpleAuthenticationInfo(token.getPrincipal(),token.getCredentials(),this.getName());
      //
      //             }else {
      //                 throw new IncorrectCredentialsException("用户不存在");
      //             }
      //
      //         }
      
      
      
      
          }
      }
      
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26
      • 27
      • 28
      • 29
      • 30
      • 31
      • 32
      • 33
      • 34
      • 35
      • 36
      • 37
      • 38
      • 39
      • 40
      • 41
      • 42
      • 43
      • 44
      • 45
      • 46
      • 47
      • 48
      • 49
      • 50
      • 51
      • 52
      • 53
      • 54
      • 55
      • 56
      • 57
      • 58
      • 59
      • 60
      • 61
      • 62
      • 63
      • 64
      • 65
      • 66
      • 67
      • 68
      • 69
      • 70
      • 71
      • 72
      • 73
      • 74
      • 75
      • 76
      • 77
      • 78
      • 79

      或者:MyRealm.java

      public class MyRealm extends AuthorizingRealm {
      
          @Autowired
      
          private UserService userService;
      
          @Autowired
      
          private RoleService roleService;
      
          @Autowired
      
          private ResourceService resourceService;
      
      
          /**
      
           * -资源授权器
      
           */
      
          @Override
      
          protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
      
              // 授权类
      
              SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
      
              
      
              User user = (User) principals.getPrimaryPrincipal();
      
              if (user == null) {
      
                  return null;
      
              }
      
              
      
              List<Role> roles = Optional
      
                      .ofNullable(roleService.getRolesByUserId(user.getId()))
      
                      .orElse(Collections.emptyList());
      
              roles.stream().forEach(role -> {
      
                  authorizationInfo.addRole(role.getRoleName());
      
                  List<Resource> resources = Optional
      
                          .ofNullable(resourceService.getResourcesByRoleId(role.getId()))
      
                          .orElse(Collections.emptyList());
      
                  resources.stream().forEach(resource -> {
      
                      authorizationInfo.addStringPermission(resource.getPermission());
      
                  });
      
              });
      
      
              return authorizationInfo;
      
          }
      
          /**
      
           * -身份验证器
      
           */
      
          @Override
      
          protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
      
              String userName = (String) token.getPrincipal();
      
              User user = userService.getUserByUserName(userName);
      
              if (user == null) {
      
                  throw new UnknownAccountException("The account do not exist.");
      
              }
      
              
      
              // realmName: 当前 realm 对象的唯一名字. 调用父类的 getName() 方法
      
              return new SimpleAuthenticationInfo(user, user.getPassword(), getName());
      
          }
      
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26
      • 27
      • 28
      • 29
      • 30
      • 31
      • 32
      • 33
      • 34
      • 35
      • 36
      • 37
      • 38
      • 39
      • 40
      • 41
      • 42
      • 43
      • 44
      • 45
      • 46
      • 47
      • 48
      • 49
      • 50
      • 51
      • 52
      • 53
      • 54
      • 55
      • 56
      • 57
      • 58
      • 59
      • 60
      • 61
      • 62
      • 63
      • 64
      • 65
      • 66
      • 67
      • 68
      • 69
      • 70
      • 71
      • 72
      • 73
      • 74
      • 75
      • 76
      • 77
      • 78
      • 79
      • 80
      • 81
      • 82
      • 83
      • 84
      • 85
      • 86
      • 87
      • 88
      • 89
      • 90
      • 91
      • 92
      • 93
      • 94
      • 95
      • 96
      • 97
      • 98
      • 99

      LoginController

      package com.qq.controller;
      
      import com.qq.model.User;
      import com.qq.service.UserService;
      import org.apache.shiro.SecurityUtils;
      import org.apache.shiro.authc.IncorrectCredentialsException;
      import org.apache.shiro.authc.UnknownAccountException;
      import org.apache.shiro.authc.UsernamePasswordToken;
      import org.apache.shiro.subject.Subject;
      import org.springframework.beans.factory.annotation.Autowired;
      import org.springframework.stereotype.Controller;
      import org.springframework.ui.Model;
      import org.springframework.web.bind.annotation.RequestMapping;
      
      import javax.servlet.http.HttpSession;
      
      /**
       * @description:com.qq.controller_studyssm
       * @author: 霏宇
       * @time: 2022/8/4,14:02
       */
      @Controller
      @RequestMapping("/login")
      public class LoginController {
          @Autowired
          UserService userService;
          @RequestMapping("/view")
          String index(){
              return "login/view";
          }
      
          @RequestMapping("/enter")
          String enter(String name, String password, HttpSession session, Model model){
      
      
              Subject subject = SecurityUtils.getSubject();     //
              UsernamePasswordToken token = new UsernamePasswordToken(name, password); //对(用户名、密码)登录方式,用UsernamePasswordToken封装即可
              try {
                  subject.login(token);	//调用login()方法。 有可能遭遇两种异常,代表登陆失败。
                  User user =userService.selectByNamePassword(name,password);
                  session.setAttribute("USER",user);
      
              } catch (UnknownAccountException e) {
                  e.printStackTrace();
                  model.addAttribute("message", "用户名错误!");
                  return "redirect:view";
              } catch (IncorrectCredentialsException e) {
                  e.printStackTrace();
                  model.addAttribute("message", "密码错误");
      
      
                  return "redirect:view";
              }
      
              System.out.println(subject.isAuthenticated()); //打印下看看是否已认证成功
      
              return "redirect:/back/hui/index";
      
      
      //        User user = userService.selectByNamePassword(name, password);
      //        if(user==null){
      //
      //            return "redirect:index";
      //        }else {
      //            session.setAttribute("USER", user);
      //            return "redirect:/back/book/index";
      //        }
      
          }
          @RequestMapping("/logout")
          String logout(HttpSession session){
              session.removeAttribute("USER");
               Subject subject =SecurityUtils.getSubject();
               subject.logout();
             // session.invalidate();
              return "redirect:view";
          }
      
      }
      
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26
      • 27
      • 28
      • 29
      • 30
      • 31
      • 32
      • 33
      • 34
      • 35
      • 36
      • 37
      • 38
      • 39
      • 40
      • 41
      • 42
      • 43
      • 44
      • 45
      • 46
      • 47
      • 48
      • 49
      • 50
      • 51
      • 52
      • 53
      • 54
      • 55
      • 56
      • 57
      • 58
      • 59
      • 60
      • 61
      • 62
      • 63
      • 64
      • 65
      • 66
      • 67
      • 68
      • 69
      • 70
      • 71
      • 72
      • 73
      • 74
      • 75
      • 76
      • 77
      • 78
      • 79
      • 80

      或者在

      UserService

      public Result<User> login(User user);
      
      • 1

    UserServiceImpl

        ```java
        @Service
        public class UserServiceImpl implements UserService{
            
        @Override
        
        public ResultEntity login(User user) {
        
            Subject subject = SecurityUtils.getSubject();
        
            UsernamePasswordToken token = new UsernamePasswordToken(user.getUserName(), 
        
                    MD5Util.getMD5(user.getPassword()));
        
            token.setRememberMe(user.getRememberMe());
            return new Result(Result.ResultStatus.SUCCESS.code, "Success", user);
        
            try {
        
                subject.login(token);
        
                subject.checkRoles();
        
            } catch (Exception e) {
        
                e.printStackTrace();
        
                  return new Result(Result.ResultStatus.FAILD.code, e.getMessage());
        
            }
            
            Session session = subject.getSession();
        
            session.setAttribute("user", subject.getPrincipal());
        
            
        
        }
        
        
        @GetMapping("/logout")
        
        public String logout() {
            Subject subject = SecurityUtils.getSubject();
            subject.logout();
           
            return "redirect:/login";
        
        }
        
        }
        ```
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52

    Spring Boot Shiro

    POM文件

    
            <dependency>
                <groupId>org.apache.shirogroupId>
                <artifactId>shiro-coreartifactId>
                <version>1.4.0version>
            dependency>
            <dependency>
                <groupId>org.apache.shirogroupId>
                <artifactId>shiro-springartifactId>
                <version>1.4.0version>
            dependency>
            
            
            <dependency>
                <groupId>org.springframework.bootgroupId>
                <artifactId>spring-boot-starter-thymeleafartifactId>
            dependency>
            
            
            <dependency>
                <groupId>com.github.theborakompanionigroupId>
                <artifactId>thymeleaf-extras-shiroartifactId>
                <version>2.0.0version>
            dependency>
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    在application.properties设置shiro配置

    #for shiro
    #开启Shiro配置,默认为true
    shiro.web.enabled=true
    
    #server.servlet.context-path=/shiro
    #spring.application.name=shiro
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    MyRealm.java

    package com.feiyu.sprtingboot.config.shiro;
    
    import com.feiyu.sprtingboot.modules.account.entity.Resource;
    import com.feiyu.sprtingboot.modules.account.entity.Role;
    import com.feiyu.sprtingboot.modules.account.entity.User;
    import com.feiyu.sprtingboot.modules.account.service.ResourceService;
    import com.feiyu.sprtingboot.modules.account.service.RoleService;
    import com.feiyu.sprtingboot.modules.account.service.UserService;
    import org.apache.shiro.authc.*;
    import org.apache.shiro.authz.AuthorizationInfo;
    import org.apache.shiro.authz.SimpleAuthorizationInfo;
    import org.apache.shiro.realm.AuthorizingRealm;
    import org.apache.shiro.subject.PrincipalCollection;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Component;
    import java.util.List;
    
    
    /**
     * @description:com.feiyu.sprtingboot.config.shiro_sprtingboot
     * @author: 霏宇
     * @time: 2022/8/31,9:41
     */
    @Component
    public class MyRealm  extends AuthorizingRealm {
       @Autowired
       private UserService userService;
       @Autowired
       private RoleService roleService;
       @Autowired
       private ResourceService resourceService;
    
        @Override
        protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
    
            SimpleAuthorizationInfo authorization = new SimpleAuthorizationInfo();
    
            // 从认证中获取当前的信息
            User user = (User) principals.getPrimaryPrincipal();
    
            // 从数据库查询当前用户的角色列表,并装载到资源授权器里
            List<Role> roles = roleService.getRolesByUserId(user.getId());
            roles.stream().forEach(item -> {
                authorization.addRole(item.getRoleName());
    //            authorization.addStringPermission(item.getRoleName());
    
                // 再去查询每个角色拥有的资源列表,并装载到资源授权器里
                List<Resource> resources = resourceService.getResourcesByRoleId(item.getId());
                resources.stream().forEach(it -> {
                    authorization.addStringPermission(it.getPermission());
                    System.out.println(it.getPermission());
    
                });
            });
    
            return authorization;
        }
    
        @Override
        protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token)
                throws AuthenticationException {
            // 获取用户名
            String userName = (String) token.getPrincipal();
    
            // 通过用户名查找数据库的 user 信息
            User user = userService.getUserByUserName(userName);
            if (user == null) {
                throw new UnknownAccountException("User name is not exit.");
            }
    
            // 封装身份验证器
            return new SimpleAuthenticationInfo(user, user.getPassword(), getName());
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75

    ShiroConfig.java

    package com.feiyu.sprtingboot.config.shiro;
    
    import at.pollux.thymeleaf.shiro.dialect.ShiroDialect;
    import org.apache.shiro.mgt.SecurityManager;
    import org.apache.shiro.spring.LifecycleBeanPostProcessor;
    import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
    import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
    import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
    import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.DependsOn;
    
    import java.util.LinkedHashMap;
    import java.util.Map;
    
    /**
     * @description:com.feiyu.sprtingboot.config.shiro_sprtingboot
     * @author: 霏宇
     * @time: 2022/8/31,10:21
     */
    @Configuration
    public class ShiroConfig {
        @Autowired
        private MyRealm myRealm;
    
        @Bean
        public SecurityManager securityManager() {
            DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
            securityManager.setRealm(myRealm);
    
            return securityManager;
        }
    
        /**
         * anon:匿名访问,无需登录 ---- AnonymousFilter
         * authc:登录后才能访问 ---- FormAuthenticationFilter
         * user:登录过能访问 ---- UserFilter
         * logout:登出 ---- LogoutFilter
         */
        @Bean
        public ShiroFilterFactoryBean shiroFilterFactoryBean() {
            ShiroFilterFactoryBean filterFactory = new ShiroFilterFactoryBean();
            // 注入安全管理器
            filterFactory.setSecurityManager(securityManager());
    
            // 设置登录页面、登录成功页面
            filterFactory.setLoginUrl("/login");
            filterFactory.setSuccessUrl("/test/thymeleafTest");
    
            // 设置其余地址的访问策略
            Map<String, String> filterMap = new LinkedHashMap<>();
            // 匿名策略
            // 登录注册
            filterMap.put("/login", "anon");
            filterMap.put("/register", "anon");
            // 静态资源
            filterMap.put("/favicon.ico", "anon");
            filterMap.put("/css/**", "anon");
            filterMap.put("/images/**", "anon");
            filterMap.put("/js/**", "anon");
            filterMap.put("/vendors/**", "anon");
            filterMap.put("/static/**", "anon");
            // 测试模块
            filterMap.put("/test/**", "anon");
            // api
            filterMap.put("/api/**", "anon");
    
            // 非匿名策略
            filterMap.put("/**", "authc");
            filterFactory.setFilterChainDefinitionMap(filterMap);
    
            return filterFactory;
        }
    
        /**
         * - 注册shiro方言,让 thymeleaf 支持 shiro 标签
         */
        @Bean
        public ShiroDialect shiroDialect(){
            return new ShiroDialect();
        }
    
        @Bean(name="lifecycleBeanPostProcessor")
        public static LifecycleBeanPostProcessor getLifecycleBeanPostProcessor() {
            return new LifecycleBeanPostProcessor();
        }
    
        /**
         * DefaultAdvisorAutoProxyCreator, Advisor 代理类生成器
         */
        @Bean
        @DependsOn({"lifecycleBeanPostProcessor"})
        public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator(){
            DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
            advisorAutoProxyCreator.setProxyTargetClass(true);
            return advisorAutoProxyCreator;
        }
    
        /**
         * - 创建 AuthorizationAttributeSourceAdvisor,扫描 Shiro 注解
         */
        @Bean
        public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(){
            AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
            authorizationAttributeSourceAdvisor.setSecurityManager(securityManager());
            return authorizationAttributeSourceAdvisor;
        }
    
    
    
    
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    如果需要加入rememberMeCookie
    /**
         * -- Remember Me Cookie
         */
        @Bean
    
        public SimpleCookie rememberMeCookie() {
    
            //这个参数是 cookie 的名称
    
            SimpleCookie simpleCookie = new SimpleCookie("rememberMe");
    
            //如果 httyOnly 设置为 true,则客户端不会暴露给客户端脚本代码,
    
            //使用 HttpOnly cookie 有助于减少某些类型的跨站点脚本攻击;
    
            simpleCookie.setHttpOnly(true);
    
            //记住我 cookie 生效时间,单位是秒
    
            simpleCookie.setMaxAge(1 * 24 * 60 * 60);
    
            return simpleCookie;
    
        }
    
       /**
         * -- 管理器
         */
    
        @Bean
        public CookieRememberMeManager rememberMeManager() {
    
            CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();
    
            cookieRememberMeManager.setCookie(rememberMeCookie());
    
            byte[] cipherKey = Base64.decode("wGiHplamyXlVB11UXWol8g==");
    
            cookieRememberMeManager.setCipherService(new AesCipherService());
    
            cookieRememberMeManager.setCipherKey(cipherKey);
    
            return cookieRememberMeManager;
        }
    
        /**
    
         * sessionCookie
    
         */
    
        @Bean
        public SimpleCookie sessionCookie() {
    
            SimpleCookie simpleCookie = new SimpleCookie("shiro.sesssion");
    
            simpleCookie.setPath("/");
    
            simpleCookie.setHttpOnly(true);
    
            simpleCookie.setMaxAge(1 * 24 * 60 * 60);
    
            return simpleCookie;
    
        }DefaultWebSessionManager 加入
         /**
         * DefaultAdvisorAutoProxyCreator, Advisor 代理类生成器
         Shiro 默认 Cookie 名称是 JSESSIONID,与 Tomcat 等默认JSESSIONID 冲突,我们需要为 
          Shiro 指定一个不同名称的 Session id,否则抛出 UnknownSessionException: There is no session with id 异常
         */
        @Bean
        @DependsOn({"lifecycleBeanPostProcessor"})
        public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator(){
            DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
        
             sessionManager.setSessionIdCookie(sessionCookie());
        
            advisorAutoProxyCreator.setProxyTargetClass(true);
           // 相隔多久检查一次 session 的有效性
           // sessionManager.setSessionValidationInterval(1 * 24 * 60 * 60 * 1000);
           // session 有效时间
          // sessionManager.setGlobalSessionTimeout(1 * 24 * 60 * 60 * 1000);
            return advisorAutoProxyCreator;
        }SecurityManager 加入
         @Bean
        public SecurityManager securityManager() {
            DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
            securityManager.setRealm(myRealm);
            securityManager.setRememberMeManager(rememberMeManager());
            securityManager.setSessionManager(sessionManager());
            return securityManager;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96

    身份验证

    UserServiceImpl

    @Override
        public Result<User> login(User user) {
    
            // 得到 subject
            Subject subject = SecurityUtils.getSubject();
    
            // 封装一个登录令牌(装载用户名和密码)
            UsernamePasswordToken token = new UsernamePasswordToken(
                    user.getUserName(),
                    MD5Util.encode(user.getPassword()));
    
            try {
                // subject.login()
                subject.login(token);
                subject.checkRoles();
                // 获取当前用户,并将之设置到 session 中
                User temp = (User) subject.getPrincipal();
                Session session = subject.getSession();
                session.setAttribute("user", temp);
    
                return new Result<User>(Result.ResultStatus.SUCCESS.code, "Success", temp);
            } catch (Exception e) {
                e.printStackTrace();
                LOGGER.debug(e.getMessage());
                return new Result<User>(Result.ResultStatus.FAILD.code, e.getMessage());
            }
    
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    Role 配合页面标签,控制不同用户访问不同页面
     /**
         * 127.0.0.1/account/users---- get
         */
        @RequiresRoles(value = {"admin","manager"},logical = Logical.OR)
        @GetMapping(value = "/account/users")
        public String userPage(ModelMap modelMap) {
            modelMap.addAttribute("template",
                                  "account/users");
            return "index";
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    Resource 配合控制器注解,控制某个特殊的授权权限
     @RequiresPermissions("/api/user/{id}")
        public Result<Object> deleteUserById(@PathVariable int id){
            return userService.deleteUserById(id);
    
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    Html页面

     <ul class="nav child_menu">
                    <li shiro:hasAnyRoles="admin,manager,staff"><a href="javascript:void(0);">Profilea>li>
                    <li shiro:hasAnyRoles="admin,manager"><a href="/account/users">Usersa>li>
                    <li shiro:hasAnyRoles="admin,manager"><a href="/account/roles">Rolesa>li>
                    <li shiro:hasAnyRoles="admin,manager"><a href="/account/resources">Resourcesa>li>
         ul>
    
    
     <ul class="nav child_menu">
                    <shiro:hasPermission name="/test/thymeleafTest">
                        <li shiro:hasAnyRoles="admin,manager,staff">
                            <a href="/test/thymeleafTest">ThymeleafTesta>li>
                    shiro:hasPermission>
                    <shiro:hasPermission name="/test/vueTest">://用户需要拥有某资源;进行显示
                        <li shiro:hasAnyRoles="admin,manager,staff"><a href="/test/vueTest">VueTesta>li>
                    shiro:hasPermission>
                    <li shiro:hasAnyRoles="admin,manager,staff">
                        <a>二级菜单<span class="fa fa-chevron-down">span>a>
                        <ul class="nav child_menu">
                            <li class="sub_menu"><a href="javascript:void(0);">三级菜单一a>li>
                            <li><a href="javascript:void(0);">三级菜单二a>li>
                        ul>
                    li>
                ul>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    在这里插入图片描述

    部分转载于http://www.sfac.xyz:8000/notes/Java/Apache_Shiro.html

  • 相关阅读:
    ipv6一致性-NDP测试
    Spring学习(1) 初识Spring
    Leetcode 1379.找出克隆二叉树中的相同节点
    [SystemC]Primitive Channels and the Kernel
    WinMerge-代码比较工具
    我们这样的人
    CCPlotR | 轻松拿捏单细胞分析之细胞交互!~
    Python: 每日一题之特殊时间
    随机化算法
    http://39.98.219.132 题库标准答案(题库序号:1969)之母舰
  • 原文地址:https://blog.csdn.net/qq_53886038/article/details/126694724