• SpringBoot 整合 Shiro 权限框架


    Shiro概述

    Shiro介绍

    Apache Shiro 是一个功能强大且易于使用的 Java 安全(权限)框架。Shiro 可以完成:认证授权加密会话管理与 Web 集成缓存 等。借助 Shiro 您可以快速轻松地保护任何应用程序——从最小的移动应用程序到最大的 Web 和企业应用程序。
    在这里插入图片描述

    官方网址: https://shiro.apache.org/

    基本功能

    在这里插入图片描述

    • Authentication:身份认证/登录,验证用户是不是拥有相应的身份;
    • Authorization:授权,即权限验证,验证某个已认证的用户是否拥有某个权限;
    • Session Manager:会话管理,即用户登录后就是一次会话,在没有退出之前,它的所有 信息都在会话中;会话可以是普通 JavaSE 环境,也可以是 Web 环境的;
    • Cryptography:加密,保护数据的安全性,如密码加密存储到数据库,而不是明文存储;
    • Web Support:Web 支持,可以非常容易的集成到 Web 环境;
    • Caching:缓存,比如用户登录后,其用户信息、拥有的角色/权限不必每次去查,这样可 以提高效率;
    • Concurrency:Shiro 支持多线程应用的并发验证,即如在一个线程中开启另一个线程,能把权限自动传播过去;、
    • Testing:提供测试支持;
    • Run As:允许一个用户假装为另一个用户(如果他们允许)的身份进行访问;
    • Remember Me:记住我,这个是非常常见的功能,即一次登录后,下次再来的话不用登录了

    Shiro架构

    在这里插入图片描述

    • Subject:任何可以与应用交互的“用户”;
    • SecurityManager :相当于 SpringMVC 中的 DispatcherServlet;是 Shiro 的心脏; 所有具体的交互都通过 SecurityManager 进行控制;它管理着所有 Subject、且负责进 行认证、授权、会话及缓存的管理。
    • Authenticator:负责 Subject 认证,是一个扩展点,可以自定义实现;可以使用认证 策略(Authentication Strategy),即什么情况下算用户认证通过了;
    • Authorizer:授权器、即访问控制器,用来决定主体是否有权限进行相应的操作;即控 制着用户能访问应用中的哪些功能;
    • Realm:可以有 1 个或多个 Realm,可以认为是安全实体数据源,即用于获取安全实体 的;可以是 JDBC 实现,也可以是内存实现等等;由用户提供;所以一般在应用中都需要 实现自己的 Realm;
    • SessionManager:管理 Session 生命周期的组件;而 Shiro 并不仅仅可以用在 Web 环境,也可以用在如普通的 JavaSE 环境
    • CacheManager:缓存控制器,来管理如用户、角色、权限等的缓存的;因为这些数据基本上很少改变,放到缓存中后可以提高访问的性能
    • Cryptography:密码模块,Shiro 提高了一些常见的加密组件用于如密码加密/解密。

    SpringBoot整合Shiro

    环境搭建

    引入依赖

    <dependency>
     <groupId>org.apache.shirogroupId>
     <artifactId>shiro-spring-boot-web-starterartifactId>
     <version>1.9.0version>
     dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5

    指定登录路径

    shiro:
     loginUrl: /user/login
    
    • 1
    • 2

    创建数据库
    user表
    在这里插入图片描述
    role表
    在这里插入图片描述
    user_role表
    在这里插入图片描述
    permissions表
    在这里插入图片描述
    role_permissions表
    在这里插入图片描述

    登录、授权、角色认证实现

    省略Mybatis查询数据库代码

    自定义实现 Realm

    @Component("authorizer")
    public class MyRealm extends AuthorizingRealm {
    
        @Autowired
        private UserService userService;
    
    
        /**
         * 自定义授权方法
         * @param principalCollection
         * @return
         */
        @Override
        protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
            //1 创建对象,存储当前登录的用户的权限和角色
            SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
            //2 获取当前用户身份信息
            String name = principalCollection.getPrimaryPrincipal().toString();
            List<String> roles = userService.getUserRoles(name);
            //3 存储角色
            info.addRoles(roles);
            //4 获取用户角色的权限信息
            List<String> permissions=new ArrayList<>();
            for (String role : roles) {
                List<String> ps = userService.getUserPermission(role);
                permissions.addAll(permissions);
            }
            //5 存储权限信息
            info.addStringPermissions(permissions);
            //6 返回
            return info;
        }
    
        /**
         * 自定义登录方法
         * @param authenticationToken
         * @return
         * @throws AuthenticationException
         */
        @Override
        protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
            //获取用户身份信息
            String name = authenticationToken.getPrincipal().toString();
            //查询用户信息
            User user = userService.getUserByName(name);
            if (user!=null){
                AuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(
                        name,//用户名
                        user.getPwd(),//密码
                        ByteSource.Util.bytes("salt"),//加盐字符
                        getName()//realm名称
                );
                return authenticationInfo;
            }
            return null;
        }
    
    }
    
    • 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

    Shiro配置类

    @Configuration
    public class ShiroConfig {
    
        //配置自定义Realm
        @Bean
        public MyRealm myRealm(){
            return new MyRealm();
        }
    
        //配置 SecurityManager
        @Bean
        public DefaultWebSecurityManager defaultWebSecurityManager(){
            //1 创建 defaultWebSecurityManager 对象
            DefaultWebSecurityManager manager = new DefaultWebSecurityManager();
            //2 创建加密对象,并设置相关属性
            HashedCredentialsMatcher matcher = new HashedCredentialsMatcher();
            //2.1 采用 md5 加密
            matcher.setHashAlgorithmName("md5");
            //2.2 迭代加密次数
            matcher.setHashIterations(3);
            //3 将加密对象存储到 myRealm 中
            myRealm().setCredentialsMatcher(matcher);
            //4 将 myRealm 存入 defaultWebSecurityManager 对象
            manager.setRealm(myRealm());
            //5 返回 defaultWebSecurityManager 对象
            return manager;
        }
    
        //配置 Shiro 内置过滤器拦截范围
        @Bean
        public DefaultShiroFilterChainDefinition shiroFilterChainDefinition(){
            DefaultShiroFilterChainDefinition definition = new DefaultShiroFilterChainDefinition();
            //设置不认证可以访问的资源
            definition.addPathDefinition("/user/login","anon");
            //配置退出过滤器
            definition.addPathDefinition("/logout","logout");
            //设置需要进行登录认证的拦截范围
            definition.addPathDefinition("/**","authc");
            return definition;
        }
    }
    
    • 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

    controller代码

    @RestController
    @RequestMapping("/user")
    @ResponseBody
    public class UserController {
    
        @PostMapping(path = "/login",produces = "application/json")
        public String login(@RequestBody User user){
            try {
                //1 获取 Subject 对象
                Subject subject = SecurityUtils.getSubject();
                //2 封装请求数据到 token 对象中
                UsernamePasswordToken token = new UsernamePasswordToken(user.getName(),user.getPwd());
                subject.login(token);
                return "登录成功!";
            }
            catch (LockedAccountException e){
                return "账号封禁";
    
            }
            catch (UnknownAccountException e) {
                e.printStackTrace();
                return  "用户不存在";
            }
            catch (IncorrectCredentialsException e) {
                e.printStackTrace();
                return  "密码错误";
            }
            catch (AuthenticationException e) {
                e.printStackTrace();
                return "登录失败!";
            }
        }
    
        //登录认证验证角色
        @RequiresRoles("admin")
        @GetMapping("/roles")
        public String userLoginRoles() {
            return "验证角色成功";
        }
    
        //登录认证验证权限
        @RequiresPermissions("user:delete")
        @GetMapping("/permissions")
        public String userLoginPermissions() {
            return "验证权限成功";
        }
    }
    
    • 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

    权限异常处理

    @ControllerAdvice
    @ResponseBody
    public class PermissionsException {
    
    
        @ExceptionHandler(UnauthorizedException.class)
        public String unauthorizedException(Exception ex){
            return "无权限";
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    多个 realm 的认证策略设置

    AuthenticationStrategy class描述
    AtLeastOneSuccessfulStrategy只要有一个(或更多)的 Realm 验证成功,那么认证将视为成功
    FirstSuccessfulStrategy第一个 Realm 验证成功,整体认证将视为成功,且后续 Realm 将被忽略
    AllSuccessfulStrategy所有 Realm 成功,认证才视为成功

    ModularRealmAuthenticator 内置的认证策略默认实现是 AtLeastOneSuccessfulStrategy 方式。

    //配置 SecurityManager
    @Bean
    public DefaultWebSecurityManager defaultWebSecurityManager(){
     //1 创建 defaultWebSecurityManager 对象
     DefaultWebSecurityManager defaultWebSecurityManager = new 
     DefaultWebSecurityManager();
     //2 创建认证对象,并设置认证策略
     ModularRealmAuthenticator modularRealmAuthenticator = new 
     ModularRealmAuthenticator();
     modularRealmAuthenticator.setAuthenticationStrategy(new 
     AllSuccessfulStrategy());
     
    defaultWebSecurityManager.setAuthenticator(modularRealmAuthenticator);
     //3 封装 myRealm 集合
     List<Realm> list = new ArrayList<>();
     list.add(myRealm1);
     list.add(myRealm2);
     //4 将 myRealm 存入 defaultWebSecurityManager 对象
     defaultWebSecurityManager.setRealms(list);
     //5 返回
     return defaultWebSecurityManager;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    会话管理

    SessionManager由SecurityManager管理。Shiro提供了三种实现:

    • DefaultSessionManager:用于JavaSE环境
    • ServletContainerSessionManager:用于web环境,直接使用Servlet容器的会话
    • DefaultWebSessionManager:用于web环境,自己维护会话(不使用Servlet容器的
      会话管理)

    获得session方式

    Session session = SecurityUtils.getSubject().getSession();
    session.setAttribute(“key”,”value”)
    
    • 1
    • 2
    • Controller 中的 request,在 shiro 过滤器中的 doFilerInternal 方法,被包装成
      ShiroHttpServletRequest。
    • SecurityManager 和 SessionManager 会话管理器决定 session 来源于ServletRequest还是由 Shiro 管理的会话。无论是通过 request.getSession 或subject.getSession 获取到 session,操作session,两者都是等价的。

    学习内容来自尚硅谷

  • 相关阅读:
    十、children的深入用法-React.Children对象上的方法
    阿里云新品通用算力型u1实例云服务器怎么样?适用于哪些场景?
    7.MySQL复合查询
    最短路算法总结
    Java爬虫的使用案例及简单总结
    Elasticsearch 保姆级入门篇
    vscode 配置默认shell
    聚类分析的基本概念和方法
    5 分钟教你搭建「视频动作分类」系统
    用Python做了几个爬虫私活,赚了
  • 原文地址:https://blog.csdn.net/qq_54429571/article/details/127995767