关于SpringBoot整合Shiro并入redis缓存

2022-08-12 08:05:27

关于SpringBoot整合Shiro并入redis缓存

最近做一个小项目加入shiro权限框架, Shiro是Apache下的一个开源项目,提供了认证、授权、加密、会话管理,与spring Security 一样都是做一个权限的安全框架,但是与Spring Security 相比,在于 Shiro 使用了比较简单易懂易于使用的授权方式。

记录一下开发过程。。。

1.开发环境:

       JDK:1.8 
       SpringBoot:2.4.4

2. 加入依赖:

<!--springboot中的redis依赖--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency><!--        使用jedis需要对新版本的redis进行屏蔽<exclusions>--><dependency><groupId>redis.clients</groupId><artifactId>jedis</artifactId><version>2.8.0</version></dependency><!--shiro权限--><dependency><!--session持久化插件--><groupId>org.crazycake</groupId><artifactId>shiro-redis</artifactId><version>2.4.2.1-RELEASE</version></dependency><dependency><groupId>org.apache.shiro</groupId><artifactId>shiro-ehcache</artifactId><version>1.4.0</version></dependency><dependency><!--spring shiro依赖--><groupId>org.apache.shiro</groupId><artifactId>shiro-spring</artifactId><version>1.4.1</version></dependency>

3. ShiroConfiguration-Shiro 配置

package com.carshow.data.shior;import java.util.LinkedHashMap;import java.util.Map;import com.carshow.data.common.Common;import org.apache.shiro.mgt.SecurityManager;import org.apache.shiro.session.mgt.eis.JavaUuidSessionIdGenerator;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.apache.shiro.web.session.mgt.DefaultWebSessionManager;import org.crazycake.shiro.RedisCacheManager;import org.crazycake.shiro.RedisManager;import org.crazycake.shiro.RedisSessionDAO;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;import org.springframework.beans.factory.annotation.Value;import org.springframework.boot.web.servlet.FilterRegistrationBean;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.context.annotation.DependsOn;import org.springframework.stereotype.Component;import org.springframework.web.filter.DelegatingFilterProxy;import javax.servlet.Filter;/**
 * @Author xw
 * @Description Shiro 配置
 * @Date 2021/4/8  15:52
 */@ConfigurationpublicclassShiroConfiguration{privatestaticfinal Logger logger= LoggerFactory.getLogger(ShiroConfiguration.class);@Value("${spring.redis.host}")private String redisHost;@Value("${spring.redis.port}")privateint redisPort;@Value("${spring.redis.password}")private String redisPassword;@Beanpublic FilterRegistrationBeandelegatingFilterProxy(){
        FilterRegistrationBean filterRegistrationBean=newFilterRegistrationBean();
        DelegatingFilterProxy proxy=newDelegatingFilterProxy();
        proxy.setTargetFilterLifecycle(true);
        proxy.setTargetBeanName("shiroFilter");
        filterRegistrationBean.setFilter(proxy);return filterRegistrationBean;}/**
     * ShiroFilterFactoryBean 处理拦截资源文件问题。
     * 注意:单独一个ShiroFilterFactoryBean配置是或报错的,以为在
     * 初始化ShiroFilterFactoryBean的时候需要注入:SecurityManager
     *
     Filter Chain定义说明
     1、一个URL可以配置多个Filter,使用逗号分隔
     2、当设置多个过滤器时,全部验证通过,才视为通过
     3、部分过滤器可指定参数,如perms,roles
     *
     */@Bean("shiroFilter")public ShiroFilterFactoryBeanshirFilter(SecurityManager securityManager){
        logger.info("ShiroConfiguration.shirFilter()");
        ShiroFilterFactoryBean shiroFilterFactoryBean=newShiroFilterFactoryBean();// 必须设置 SecurityManager
        shiroFilterFactoryBean.setSecurityManager(securityManager);//自定义拦截器
        Map<String, Filter> filtersMap=newLinkedHashMap<String, Filter>();//限制同一帐号同时在线的个数。
        filtersMap.put("kickout",kickoutSessionControlFilter());
        shiroFilterFactoryBean.setFilters(filtersMap);//拦截器.
        Map<String,String> filterChainDefinitionMap=newLinkedHashMap<String,String>();//配置退出过滤器,其中的具体的退出代码Shiro已经替我们实现了
        filterChainDefinitionMap.put("/logout","logout");

        filterChainDefinitionMap.put("/swagger-resources/**","anon");
        filterChainDefinitionMap.put("/webjars/**","anon");
        filterChainDefinitionMap.put("/doc.html","anon");
        filterChainDefinitionMap.put("/v2/**","anon");
        filterChainDefinitionMap.put("/swagger-ui.html/**","anon");

        filterChainDefinitionMap.put("/auth/kickout","anon");// 开放所有外接接口
        filterChainDefinitionMap.put(Common.PATH_OUT_URL+"**","anon");//<!-- authc:所有url都必须认证通过才可以访问; anon:所有url都都可以匿名访问-->
        filterChainDefinitionMap.put("/**","authc,kickout");// 配器shirot认登录地址,前后端分离中登录累面跳转应由前端路由控制,后台仅返回json数据, 对应LoginController中unauth请求
        shiroFilterFactoryBean.setLoginUrl(Common.PATH_OUT_URL+"user/un_auth");// 未授权界面, 对应LoginController中 unauthorized 请求
        shiroFilterFactoryBean.setUnauthorizedUrl(Common.PATH_OUT_URL+"user/unauthorized");

        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);return shiroFilterFactoryBean;}@Beanpublic SecurityManagersecurityManager(){
        DefaultWebSecurityManager securityManager=newDefaultWebSecurityManager();
        securityManager.setRealm(myShiroRealm());// 自定义缓存实现 使用redis
        securityManager.setCacheManager(cacheManager());// 自定义session管理 使用redis
        securityManager.setSessionManager(sessionManager());return securityManager;}@Beanpublic MyShiroRealmmyShiroRealm(){
        MyShiroRealm myShiroRealm=newMyShiroRealm();return myShiroRealm;}/**
     * cacheManager 缓存 redis实现
     * 使用的是shiro-redis开源插件
     *
     * @return
     */public RedisCacheManagercacheManager(){
        RedisCacheManager redisCacheManager=newRedisCacheManager();
        redisCacheManager.setRedisManager(redisManager());
        redisCacheManager.setKeyPrefix("SPRINGBOOT_CACHE:");//设置前缀return redisCacheManager;}/**
     * 配置shiro redisManager
     * 使用的是shiro-redis开源插件
     *
     * @return
     */public RedisManagerredisManager(){
        logger.info("===============创建RedisManager,连接Redis..URL= "+ redisHost+":"+ redisPort);
        RedisManager redisManager=newRedisManager();
        redisManager.setHost(redisHost);
        redisManager.setPort(redisPort);
        redisManager.setTimeout(1800);//设置过期时间
        redisManager.setPassword(redisPassword);return redisManager;}/**
     * RedisSessionDAO shiro sessionDao层的实现 通过redis
     * 使用的是shiro-redis开源插件
     */@Beanpublic RedisSessionDAOredisSessionDAO(){
        RedisSessionDAO redisSessionDAO=newRedisSessionDAO();
        redisSessionDAO.setRedisManager(redisManager());
        redisSessionDAO.setSessionIdGenerator(sessionIdGenerator());
        redisSessionDAO.setKeyPrefix("SPRINGBOOT_SESSION:");return redisSessionDAO;}/**
     * Session ID 生成器
     *
     * @return JavaUuidSessionIdGenerator
     */@Beanpublic JavaUuidSessionIdGeneratorsessionIdGenerator(){returnnewJavaUuidSessionIdGenerator();}/**
     * Session Manager
     * 使用的是shiro-redis开源插件
     */@Beanpublic DefaultWebSessionManagersessionManager(){
        MySessionManager sessionManager=newMySessionManager();
        sessionManager.setSessionDAO(redisSessionDAO());return sessionManager;}/**
     * 限制同一账号登录同时登录人数控制
     *
     * @return
     */@Beanpublic KickoutSessionControlFilterkickoutSessionControlFilter(){
        KickoutSessionControlFilter kickoutSessionControlFilter=newKickoutSessionControlFilter();
        kickoutSessionControlFilter.setCacheManager(cacheManager());
        kickoutSessionControlFilter.setSessionManager(sessionManager());
        kickoutSessionControlFilter.setKickoutAfter(false);
        kickoutSessionControlFilter.setMaxSession(1);
        kickoutSessionControlFilter.setKickoutUrl("/auth/kickout");return kickoutSessionControlFilter;}/**
     *  开启Shiro的注解(如@RequiresRoles,@RequiresPermissions),需借助SpringAOP扫描使用Shiro注解的类,并在必要时进行安全逻辑验证
     * 配置以下两个bean(DefaultAdvisorAutoProxyCreator和AuthorizationAttributeSourceAdvisor)即可实现此功能
     * //自动创建代理,没有这个鉴权可能会出错
     * @return
     */@Bean@DependsOn("lifecycleBeanPostProcessor")public DefaultAdvisorAutoProxyCreatorgetDefaultAdvisorAutoProxyCreator(){
        DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator=newDefaultAdvisorAutoProxyCreator();
        defaultAdvisorAutoProxyCreator.setProxyTargetClass(true);return defaultAdvisorAutoProxyCreator;}/**
     * 开启aop注解支持
     * @param securityManager
     * @return
     */@Beanpublic AuthorizationAttributeSourceAdvisorauthorizationAttributeSourceAdvisor(SecurityManager securityManager){
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor=newAuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);return authorizationAttributeSourceAdvisor;}/**
     * Shiro生命周期处理器
     *
     */@Bean(name="lifecycleBeanPostProcessor")publicstatic LifecycleBeanPostProcessorgetLifecycleBeanPostProcessor(){returnnewLifecycleBeanPostProcessor();}}

4.身份校验核心类-MyShiroRealm

package com.carshow.data.shior;import com.carshow.data.mapper.RoleMapper;import com.carshow.data.mapper.UserMapper;import com.carshow.data.model.TPermission;import com.carshow.data.model.TRole;import com.carshow.data.model.TUser;import com.carshow.data.service.IRoleService;import com.carshow.data.service.IUserService;import com.carshow.data.service.impl.UserServiceImpl;import org.apache.shiro.authc.AuthenticationException;import org.apache.shiro.authc.AuthenticationInfo;import org.apache.shiro.authc.AuthenticationToken;import org.apache.shiro.authc.SimpleAuthenticationInfo;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.apache.shiro.util.ByteSource;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import javax.annotation.Resource;import java.util.List;import java.util.Objects;/**
 * @Author xw
 * @Description 身份校验核心类
 * @Date 2021/4/8  15:57
 */publicclassMyShiroRealmextendsAuthorizingRealm{privatestaticfinal Logger logger= LoggerFactory.getLogger(MyShiroRealm.class);privatestatic String SALT="mySlalt";@Resourceprivate IUserService userService;@Resourceprivate IRoleService roleService;/**
     * 认证信息.(身份验证)
     * :
     * Authentication 是用来验证用户身份
     * @param token
     * @return
     * @throws AuthenticationException
     */@Overrideprotected AuthenticationInfodoGetAuthenticationInfo(AuthenticationToken token)throws AuthenticationException{
        logger.info("---------------- 执行 Shiro 凭证认证 ----------------------");//获取用户的输入的账号.
        String username=(String)token.getPrincipal();//通过username从数据库中查找 User对象,如果找到,没找到.//实际项目中,这里可以根据实际情况做缓存,如果不做,Shiro自己也是有时间间隔机制,2分钟内不会重复执行该方法
        TUser user= userService.selectUserByName(username);if(Objects.isNull(user)){return null;}
        logger.info("----->>userInfo="+ user.toString());/*
         * 获取权限信息:这里没有进行实现,
         * 请自行根据UserInfo,Role,Permission进行实现;
         * 获取之后可以在前端for循环显示所有链接;
         *///userInfo.setPermissions(userService.findPermissions(user));//        userService.findRoleAndPermissions(user);//账号判断;//加密方式;//交给AuthenticatingRealm使用CredentialsMatcher进行密码匹配,如果觉得人家的不好可以自定义实现
        SimpleAuthenticationInfo authenticationInfo=newSimpleAuthenticationInfo(
                username,//用户名
                user.getPassword(),//密码
                ByteSource.Util.bytes(user.getUsername()+ SALT),//salt=username+saltgetName()//realm name);return authenticationInfo;}/**
     * 此方法调用  hasRole,hasPermission的时候才会进行回调.
     *
     * 权限信息.(授权):
     * 1、如果用户正常退出,缓存自动清空;
     * 2、如果用户非正常退出,缓存自动清空;
     * 3、如果我们修改了用户的权限,而用户不退出系统,修改的权限无法立即生效。
     * (需要手动编程进行实现;放在service进行调用)
     * 在权限修改后调用realm中的方法,realm已经由spring管理,所以从spring中获取realm实例,
     * 调用clearCached方法;
     * :Authorization 是授权访问控制,用于对用户进行的操作授权,证明该用户是否允许进行当前操作,如访问某个链接,某个资源文件等。
     * @param principals
     * @return
     */@Overrideprotected AuthorizationInfodoGetAuthorizationInfo(PrincipalCollection principals){

        logger.info("开始执行授权操作-->MyShiroRealm.doGetAuthorizationInfo()");

        SimpleAuthorizationInfo authorizationInfo=newSimpleAuthorizationInfo();//上面是用户名,此处就获取用户名,,上面是user,此处就获取user//   TUser user  = (TUser)principals.getPrimaryPrincipal();
        TUser user=userService.selectUserByName(principals.getPrimaryPrincipal().toString());// 查询用户角色和权限
        List<TRole> roles= roleService.getUserRoles(user.getId());///在认证成功之后返回.//设置角色信息.//支持 Set集合,//用户的角色对应的所有权限for(TRole role:roles){
            authorizationInfo.addRole(role.getName());for(TPermission p:role.getPermissions()){
                authorizationInfo.addStringPermission(p.getCode());}}return authorizationInfo;}}

5. 自定义session管理-MySessionManager

package com.carshow.data.shior;import org.apache.commons.lang.StringUtils;import org.apache.shiro.web.servlet.ShiroHttpServletRequest;import org.apache
  • 作者:夕夕夕兮
  • 原文链接:https://blog.csdn.net/forever_xw_/article/details/115580153
    更新时间:2022-08-12 08:05:27