shiro基础学习篇02-集成springboot

Shiro集成SpringBoot

  • 一般企业中使用Shiro 都是配合 SpringBoot 进行集成使用。

项目集成思路

项目骨架

  • 在这里插入图片描述

项目依赖

  • <dependencies>
            <!-- shiro-springboot -->
            <dependency>
                <groupId>org.crazycake</groupId>
                <artifactId>shiro-redis-spring-boot-starter</artifactId>
                <version>3.3.1</version>
            </dependency>
    
            <!-- springboot -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
                <version>2.1.6.RELEASE</version>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-validation</artifactId>
                <version>2.1.6.RELEASE</version>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <version>2.1.6.RELEASE</version>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-data-redis</artifactId>
                <version>2.1.6.RELEASE</version>
            </dependency>
            <dependency>
                <groupId>com.baomidou</groupId>
                <artifactId>mybatis-plus-boot-starter</artifactId>
                <version>3.3.2</version>
            </dependency>
            <dependency>
                <groupId>org.apache.commons</groupId>
                <artifactId>commons-lang3</artifactId>
                <version>3.9</version>
            </dependency>
    
            <!-- database -->
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>druid-spring-boot-starter</artifactId>
                <version>1.1.23</version>
            </dependency>
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <version>8.0.23</version>
            </dependency>
    
            <!-- hutool -->
            <dependency>
                <groupId>cn.hutool</groupId>
                <artifactId>hutool-crypto</artifactId>
                <version>5.6.0</version>
            </dependency>
            <dependency>
                <groupId>cn.hutool</groupId>
                <artifactId>hutool-http</artifactId>
                <version>5.6.0</version>
            </dependency>
    
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>fastjson</artifactId>
                <version>1.2.73</version>
            </dependency>
    
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <version>1.16.10</version>
                <scope>provided</scope>
            </dependency>
    
        </dependencies>
    
        <!-- 设置阿里镜像仓库 -->
        <repositories>
            <repository>
                <!--远程仓库唯一标识符。可以用来匹配在settings.xml文件里配置的远程仓库 -->
                <id>nexus-aliyun</id>
                <!--远程仓库名称 -->
                <name>Nexus aliyun</name>
                <!--远程仓库URL,按protocol://hostname/path形式 -->
                <url>http://maven.aliyun.com/nexus/content/groups/public</url>
            </repository>
        </repositories>
    
        <!-- 过滤xml文件 -->
        <build>
            <resources>
                <resource>
                    <directory>src/main/java</directory>
                    <includes>
                        <include>**/*.xml</include>
                    </includes>
                    <filtering>false</filtering>
                </resource>
            </resources>
        </build>
    

ShiroDbRealm

  • 主要创建自定义类 继承 AuthorizingRealm 实现自定义的 认证, 授权方法。

  • 主要的权限数据从数据库中获取。

  • 在这里插入图片描述

  • 代码示例:

  • @Slf4j
    public class UserRealm extends AuthorizingRealm {
    
        @Autowired
        private UserMapper userMapper;
    
        @Autowired
        private ResourceMapper resourceMapper;
    
        @Autowired
        private RoleMapper roleMapper;
    
        /**
        * 这个方法只有会在登录时调用一次
        *  认证方法
        */
        @Override
        protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
            UserLoginToken userLoginToken = (UserLoginToken) authenticationToken;
            // 1. 查询数据库数据是否匹配
            String username = userLoginToken.getUsername();
            char[] password = userLoginToken.getPassword();
            User user = userMapper.queryByUserName(username);
            if (Objects.isNull(user)) {
                throw new UnknownAccountException("账号不存在!");
            }
            // 2. 返回匹配方法,进行密码的校验
            ByteSource source = ByteSource.Util.bytes(password);
            return new SimpleAuthenticationInfo(user , user.getPassWord() , source , "userRealm");
        }
    
        /**
         * 这个方法在每次过滤器进行hasRole,HasPerm 等方法时 都会调用。
         *  授权方法 , 一般只存储角色信息即可
         */
        @Override
        protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
            // 获取登录主体信息
            User user = (User)principalCollection.getPrimaryPrincipal();
            if (Objects.isNull(user)) {
                throw new RuntimeException("数据不存在!");
            }
            SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
            // 查询角色信息
            List<Role> roleList = roleMapper.queryByUserId(user.getId());
            List<String> roleIdList = roleList.stream().map(Role::getId).collect(Collectors.toList());
            Set<String> LabelList = roleList.stream().map(Role::getLabel).collect(Collectors.toSet());
            simpleAuthorizationInfo.setRoles(LabelList);
            // 查询资源信息
            List<Resource> resourceList = resourceMapper.queryByRoleList(roleIdList);
            Set<String> permList = resourceList.stream().map(Resource::getLabel).collect(Collectors.toSet());
            simpleAuthorizationInfo.setStringPermissions(permList);
            return simpleAuthorizationInfo;
        }
    }
    

ShiroConfig

  • 通过 Configuration 创建 Shiro 的一系列配置,进行shiro的过滤权限等操作

  • Shiro的配置文件

  • 在这里插入图片描述

  • @Configuration
    @Slf4j
    public class ShiroConfig {
    
        /**
        *  会话管理器
        */
        @Bean
        public DefaultWebSessionManager getDefaultWebSessionManager() {
            DefaultWebSessionManager defaultWebSessionManager = new DefaultWebSessionManager();
            // 会话过期时间,单位:毫秒(在无操作时开始计时)
            defaultWebSessionManager.setGlobalSessionTimeout(60 * 60 * 1000);
            // 开启自动校验会话过期定时 - 对性能有影响
            defaultWebSessionManager.setSessionValidationSchedulerEnabled(true);
            // 生效cookie
            defaultWebSessionManager.setSessionIdCookieEnabled(true);
            // 设置session存储
            defaultWebSessionManager.setSessionDAO(redisSessionDAO());
            return defaultWebSessionManager;
        }
    
        /**
         *  核心权限管理器
         */
        @Bean
        public DefaultWebSecurityManager webSecurityManager(UserRealm userRealm) {
            DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
            // 设置realm
            securityManager.setRealm(userRealm);
            // 缓存管理器
            securityManager.setCacheManager(redisCacheManager());
            // 会话session管理
            securityManager.setSessionManager(getDefaultWebSessionManager());
            return securityManager;
        }
    
        /**
         *  过滤器拦截链路
         */
        @Bean
        public ShiroFilterChainDefinition shiroFilterChainDefinition() {
            DefaultShiroFilterChainDefinition chainDefinition = new DefaultShiroFilterChainDefinition();
            HashMap<String, String> listMap = new LinkedHashMap<>();
            // anon默认授权 , authc登录鉴权
            listMap.put("/static/**" , "anon");
            listMap.put("/shiro-test/user/login" , "anon");
            listMap.put("/user/update" , "customUser,customCheck,customRole[admin,SuperAdmin]");
            listMap.put("/shiro-test/**" , "authc");
            chainDefinition.addPathDefinitions(listMap);
            return chainDefinition;
        }
    
        /**
         *  过滤器
         */
        @Bean
        public ShiroFilterFactoryBean shiroFilterFactoryBean(ShiroFilterChainDefinition shiroFilterChainDefinition , DefaultWebSecurityManager securityManager) {
            // 使用默认过滤器
            ShiroFilterFactoryBean shiroFilter = new ShiroFilterFactoryBean();
            shiroFilter.setSecurityManager(securityManager);
    
            // 自定义过滤器
    //        shiroFilter.setFilters();
    
            // 设置过滤器拦截链路
            shiroFilter.setFilterChainDefinitionMap(shiroFilterChainDefinition.getFilterChainMap());
            // 设置路径
            shiroFilter.setLoginUrl("/");
            shiroFilter.setSuccessUrl("/");
            shiroFilter.setUnauthorizedUrl("/");
            return shiroFilter;
        }
    
        /**
        *  自定义Realm
        */
        @Bean
        public UserRealm userRealm() {
            UserRealm userRealm = new UserRealm();
            // 设置密码校验器
            userRealm.setCredentialsMatcher(credentialsMatcher());
            return userRealm;
        }
    
        /**
         * 管理shiro bean生命周期 , 让Shiro 的配置类可以读取Spring 的bean
         */
        @Bean
        public static LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
            return new LifecycleBeanPostProcessor();
        }
    
        /**
        *  密码校验器
        */
        private HashedCredentialsMatcher credentialsMatcher() {
            HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher();
            credentialsMatcher.setHashAlgorithmName("MD5");
            //转换次数
            credentialsMatcher.setHashIterations(1);
            return credentialsMatcher;
        }
    
        /**
         * RedisSessionDao session存储管理
        */
        private RedisSessionDAO redisSessionDAO() {
            RedisSessionDAO myRedisSessionDAO = new RedisSessionDAO();
            RedisManager redisManager = new RedisManager();
            myRedisSessionDAO.setRedisManager(redisManager);
            return myRedisSessionDAO;
        }
    
        /**
        *  Redis 权限管理器缓存
        */
        private RedisCacheManager redisCacheManager() {
            RedisCacheManager redisCacheManager = new RedisCacheManager();
            redisCacheManager.setRedisManager(new RedisManager());
            return redisCacheManager;
        }
    }
    

过滤器

  • Shiro内置很多业务默认的过滤器,比如:身份过滤器,授权过滤器等. 参考:org.apache.shiro.web.filter.mgt.DefaultFilter 中过滤器枚举.

  • 过滤器是指 Shiro 对某一url进行的拦截处理,相当于web的过滤器,进行权限相关的判断处理。

  • 自定义过滤器(继承 AuthorizationFilter 授权过滤器 ):

    • isAccessAllowed: 过滤方法 ,返回boolean类型
    • onAccessDenied:过滤失败响应方法
  • 权限校验过滤器:

  • /**
     * 授权权限校验过滤器
     */
    @Slf4j
    public class CustomRoleFilter extends AuthorizationFilter {
    
        /**
        *  校验失败方法
        */
        @Override
        protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws IOException {
            log.error("role check fail....");
            Subject subject = this.getSubject(request, response);
            // 返回错误码 , 这里可以做国际化处理
            HttpServletResponse httpServletResponse = (HttpServletResponse)response;
            Result<Void> result = Result.error("500", "没有权限");
            httpServletResponse.setHeader("Content-Type" , "application/json;charset=utf-8");
            httpServletResponse.getWriter().write(JSONObject.toJSONString(result));
            return false;
        }
    
        /**
        *  校验方法
        */
        @Override
        protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception {
            log.info("role check start...");
            Subject subject = getSubject(request, response);
            String[] rolesArray = (String[]) mappedValue;
    
            if (rolesArray == null || rolesArray.length == 0) {
                //no roles specified, so nothing to check - allow access.
                return true;
            }
            for (String role : rolesArray) {
                // Shiro的默认角色过滤器 这里用的是 hasAllRoles
                if (subject.hasRole(role)) {
                    return true;
                }
            }
            return false;
        }
    }
    
  • 用户登录过滤器:

  • @Slf4j
    public class CustomUserFilter extends UserFilter {
    
        /**
        *  没有登录过滤器响应报错
        */
        @Override
        protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
            log.error("role check fail....");
            Subject subject = this.getSubject(request, response);
            // 返回错误码 , 这里可以做国际化处理
            HttpServletResponse httpServletResponse = (HttpServletResponse)response;
            Result<Void> result = Result.error("500", "没有登录");
            httpServletResponse.setHeader("Content-Type" , "application/json;charset=utf-8");
            httpServletResponse.getWriter().write(JSONObject.toJSONString(result));
            return false;
        }
    }
    
  • 更新权限过滤器(可进行业务逻辑判断,是否需要在url权限判断前,更新权限信息):

  • @Slf4j
    public class PathCheckFilter extends PathMatchingFilter {
        @Override
        protected boolean onPreHandle(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception {
            log.info("更新权限....");
            return true;
        }
    }
    
  • ShiroConfig中配置:

  • ShiroFilterFactoryBean 类设置自定义过滤器
    // 自定义过滤器
    shiroFilter.setFilters(getCustomFilter());
    
    
    /**
    *  封装自定义过滤器
    */
    private Map<String, Filter> getCustomFilter() {
      HashMap<String, Filter> filterList = new LinkedHashMap<>();
      filterList.put("customRole" , new CustomRoleFilter());
      filterList.put("customUser" , new CustomUserFilter());
      filterList.put("customCheck" , new PathCheckFilter());
      return filterList;
    }
    
    // 设置url过滤器链路
    listMap.put("/**","customUser,customCheck,customRole[admin,SuperAdmin]");
    

过滤器链路

  • 过滤器进行拦截的路径的配置信息 , 一个 url 可以配置多个过滤器, 所以称为过滤器链(有前后顺序)。

  • 按照定义的顺序,自上而下.

  • 代码示例:
    listMap.put("/static/**" , "anon");
    listMap.put("/shiro-test/user/login" , "anon");
    listMap.put("/shiro-test/**" , "roles[user],authc");
    ---------------------------------
    /shiro-test/**:?  
    ? 表示过滤器执行的过滤器 , 以下是Shiro提供的默认常用filter过滤器
    anon 							- 静默授权
    authc 							- 登录
    logout 							- 登出
    authcBasic 						-
    noSessionCreation 				-
    perms["user:add,user:update"] 	- 权限过滤
    port 							- 端口过滤器
    rest 							- 请求的rest方法 GET / Post / put / delete
    roles["admin,user"] 			- 角色,多角色全匹配.
    ssl 							- 必须为https请求
    user 							- 登录即可
    

PathMatchingFilter

  • 单独讲一下 PathMatchingFilter 这个类。

  • 在这里插入图片描述

  • 其实就是各个过滤器的父类,一般继承这个接口,是为了实现 onPreHandle 方法,在下一步进行过滤器执行前,进行操作。返回 true 往下执行过滤器。在这里可以做一些类似更新权限的操作,当权限表内容修改时,就先更新一下 FilterChain 拦截url配置。

  • 具体的作用体现就是:通过 onPreHandle 方法来判断是否执行之后的过滤器链,配置给url时,也遵循前后顺序。

注解鉴权方式

  • 注解方式通过Shiro定义的一系列注解,一般写在 Controller 层方法上进行校验鉴权。

  • 一般不会用注解方式,代码紊乱,扩展性差,不易管理。

  • 常用注解:

  • @RequiresAuthentication			- 表名当前用户是否经过认证
    @RequiresGuest					- 需要为 guest 用户才能访问
    @RequiresPermissions			- 当前用户需要指定权限
    @RequiresRoles					- 需要拥有XXX角色
    @RequiresUser					- 需要为已认证用户
    
  • Configuration配置信息:

  • /**
    *  注解方式鉴权生效器
    */
    @Bean
    public DefaultAdvisorAutoProxyCreator DefaultAdvisorAutoProxyCreator() {
            DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
            defaultAdvisorAutoProxyCreator.setProxyTargetClass(true);
            return defaultAdvisorAutoProxyCreator;
    }
    
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(DefaultWebSecurityManager securityManager) {
            AuthorizationAttributeSourceAdvisor aas = new AuthorizationAttributeSourceAdvisor();
            aas.setSecurityManager(securityManager);
            return aas;
    }
    

重要总结

  1. 在配置过滤器时 ("/**" , XXX) 一般在最后会配置这样一个全局路径的过滤,这种形式会重复走两边Shiro的授权流程代码。具体原因未知,不加这个全局流程就不会重复。
  2. ShiroConfig中可以不配置 LifecycleBeanPostProcessor,需要给引入要使用的 SpringBean 加上 @Lazy注解,因为ShiroConfig的执行顺序在,Shiro-Redis配置文件之前。

项目路径 shiro-springboot

  • 链接:https://pan.baidu.com/s/1OotLAOubzxpZDpPLpjdFuw
    提取码:bk3p
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
对于Spring Boot集成Shiro,你可以按照以下步骤进行操作: 1. 首先,在你的Spring Boot项目中添加Shiro的依赖。你可以在pom.xml文件中添加以下依赖关系: ```xml <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring-boot-starter</artifactId> <version>1.7.1</version> </dependency> ``` 2. 创建一个Shiro的配置类,用于配置Shiro的相关组件和属性。可以使用`@Configuration`注解来标记该类作为配置类,并使用`@EnableShiroAnnotation`注解来启用Shiro的注解支持。 ```java @Configuration @EnableShiroAnnotation public class ShiroConfig { // 配置Shiro的相关组件和属性 // ... } ``` 3. 在上述配置类中,可以配置Shiro的Realm、Session管理器、缓存管理器等组件。你可以根据自己的需求选择相应的实现类并进行配置。 ```java @Configuration @EnableShiroAnnotation public class ShiroConfig { @Bean public Realm realm() { // 配置自定义的Realm实现类 // ... return realm; } @Bean public SessionManager sessionManager() { // 配置自定义的Session管理器实现类 // ... return sessionManager; } @Bean public CacheManager cacheManager() { // 配置自定义的缓存管理器实现类 // ... return cacheManager; } // 其他配置项... } ``` 4. 在主配置类中,添加`@Import`注解来引入Shiro的配置类。 ```java @SpringBootApplication @Import(ShiroConfig.class) public class YourApplication { public static void main(String[] args) { SpringApplication.run(YourApplication.class, args); } } ``` 5. 在需要进行权限控制的地方,使用Shiro的注解来标记需要进行权限验证的方法或类。例如,可以使用`@RequiresRoles`注解来限制具有特定角色的用户才能访问方法。 ```java @RestController public class YourController { @RequiresRoles("admin") @GetMapping("/admin") public String admin() { return "Hello, admin!"; } } ``` 这样,你就成功地集成Spring BootShiro,并可以进行基于角色的权限控制了。当然,以上只是一个简单的示例,你可以根据自己的需求进行更详细的配置和使用。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值