spring-boot+shiro+spring-data-redis 实战

一,根据项目要求需要对spring-mvc+shiro的项目转换成spring-boot+shiro+spring-data-redis+redis集群的平台以便于实现shiro在spring-boot中安全认证+权限控制+全局sission管理

项目结构

spring-boot+shiro+spring-data-redis 实战_第1张图片

pom.xml

xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>DC.API_WEBartifactId>
        <groupId>com.hlinkgroupId>
        <version>1.0.1version>
    parent>
    <modelVersion>4.0.0modelVersion>
    <groupId>com.hlink.webgroupId>
    <artifactId>DC.WEB_EmpartifactId>

    <properties>
        <spring-version>5.0.5.RELEASspring-version>
        <shiro-version>1.4.0shiro-version>
        <slf4j-version>1.7.3slf4j-version>
        <kafka-version>2.11kafka-version>
        <java-version>1.8java-version>
        <junit-version>4.12junit-version>
        <jackson.version>2.9.4jackson.version>
    properties>

    <dependencies>
        <dependency>
            <groupId>com.hlinkgroupId>
            <artifactId>DC.API_CommonsartifactId>
            <version>1.0.0-compilerversion>
        dependency>
        
        
        <dependency>
            <groupId>org.slf4jgroupId>
            <artifactId>slf4j-apiartifactId>
            <version>1.6.3version>
        dependency>
        <dependency>
            <groupId>org.slf4jgroupId>
            <artifactId>slf4j-log4j12artifactId>
            <version>1.7.22version>
        dependency>
        <dependency>
            <groupId>redis.clientsgroupId>
            <artifactId>jedisartifactId>
            <version>2.9.0version>
        dependency>
        <dependency>
            <groupId>com.dyuproject.protostuffgroupId>
            <artifactId>protostuff-coreartifactId>
            <version>1.0.8version>
        dependency>
        <dependency>
            <groupId>com.dyuproject.protostuffgroupId>
            <artifactId>protostuff-runtimeartifactId>
            <version>1.0.8version>
        dependency>
       
        <dependency>
            <groupId>javassistgroupId>
            <artifactId>javassistartifactId>
            <version>3.11.0.GAversion>
        dependency>
        <dependency>
            <groupId>com.sun.elgroupId>
            <artifactId>el-riartifactId>
            <version>1.0version>
        dependency>
        <dependency>
            <groupId>javax.servletgroupId>
            <artifactId>javax.servlet-apiartifactId>
            <version>4.0.0version>
        dependency>
        <dependency>
            <groupId>org.quartz-schedulergroupId>
            <artifactId>quartzartifactId>
            <version>2.3.0version>
        dependency>
        <dependency>
            <groupId>org.quartz-schedulergroupId>
            <artifactId>quartz-jobsartifactId>
            <version>2.3.0version>
        dependency>
        <dependency>
            <groupId>javax.validationgroupId>
            <artifactId>validation-apiartifactId>
            <version>1.1.0.Finalversion>
        dependency>
        <dependency>
            <groupId>org.hibernategroupId>
            <artifactId>hibernate-validatorartifactId>
            <version>5.4.1.Finalversion>
        dependency>
        <dependency>
            <groupId>org.springframework.datagroupId>
            <artifactId>spring-data-redisartifactId>
            <version>2.0.6.RELEASEversion>
        dependency>
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-webartifactId>
            <exclusions>
                <exclusion>
                    <groupId>org.springframework.bootgroupId>
                    <artifactId>spring-boot-starter-loggingartifactId>
                exclusion>
            exclusions>
            <version>2.0.2.RELEASEversion>
        dependency>
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-autoconfigureartifactId>
            <version>2.0.2.RELEASEversion>
        dependency>
        <dependency>
            <groupId>org.crazycakegroupId>
            <artifactId>shiro-redisartifactId>
            <version>2.4.2.1-RELEASEversion>
        dependency>
        
    dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.pluginsgroupId>
                <artifactId>maven-compiler-pluginartifactId>
                <version>3.6.1version>
                <configuration>
                    <source>1.8source>
                    <target>1.8target>
                configuration>
            plugin>
            <plugin>
                <groupId>org.springframework.bootgroupId>
                <artifactId>spring-boot-maven-pluginartifactId>
                <version>2.0.2.RELEASEversion>
                <configuration>
                    
                    <fork>truefork>
                    <addResources>trueaddResources>
                configuration>
            plugin>
        plugins>
        <finalName>webfinalName>
    build>
project>

spring-boot风格中spring-bean.xml中的数据源配置被省略,spring-mvc.xml中的前段控制器配置被省略,web.xml配置被省略,但是shiro的配置与spring-data-redis的配置仍需要自己配置,spring-boot推荐的方式是使用@Configuration 注解在该注解类中每个方法中使用@Bean注解的方式,大致参考:

  1. @Configuration  
  2. public class ShiroConfiguration {  
  3.     private final Logger log = LoggerFactory.getLogger(this.getClass());  
  4.   
  5.     @Bean  
  6.     public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager) {  
  7.         log.info("$--ShiroConfiguration.shirFilter()");  
  8.         ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();  
  9.         // 必须设置 SecurityManager  
  10.         shiroFilterFactoryBean.setSecurityManager(securityManager);  
  11.         //拦截器.  
  12.         Map filterChainDefinitionMap = new LinkedHashMap();  
  13.         // 配置退出过滤器,其中的具体的退出代码Shiro已经替我们实现了  
  14.         filterChainDefinitionMap.put("/logout""logout");  
  15.         // ;  
  16.         //   
  17.         filterChainDefinitionMap.put("/validatecodeServlet""anon");//验证码可以匿名访问  
  18.         filterChainDefinitionMap.put("/toPasswordReset""anon");//重置密码可以匿名访问  
  19.         filterChainDefinitionMap.put("/static/**""anon");  
  20.         filterChainDefinitionMap.put("/**""authc");//authc  
  21.         // 如果不设置默认会自动寻找Web工程根目录下的"/login.jsp"页面  
  22.         shiroFilterFactoryBean.setLoginUrl("/login");  
  23.         // 登录成功后要跳转的链接  
  24.         shiroFilterFactoryBean.setSuccessUrl("/index");  
  25.         // 未授权界面;  
  26.         shiroFilterFactoryBean.setUnauthorizedUrl("/403");  
  27.         //自定义表达验证,校验验证码  
  28.         Map filters = new HashMap();  
  29.         filters.put("authc"new MyFormAuthenticationFilter());  
  30.         shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);  
  31.         shiroFilterFactoryBean.setFilters(filters);  
  32.         return shiroFilterFactoryBean;  
  33.     }  
  34.   
  35.     @Bean  
  36.     public SecurityManager securityManager() {  
  37.         DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();  
  38.         //设置realm.  
  39.         securityManager.setRealm(myShiroRealm());  
  40.         return securityManager;  
  41.     }  
  42.       
  43.     /**  
  44.      * 身份认证realm;  
  45.      * @return  
  46.      */  
  47.     @Bean  
  48.     public MyShiroRealm myShiroRealm(){  
  49.        MyShiroRealm myShiroRealm = new MyShiroRealm();  
  50.        myShiroRealm.setCredentialsMatcher(hashedCredentialsMatcher());  
  51.        return myShiroRealm;  
  52.     }  
  53.       
  54.     /** 
  55.      * 凭证匹配器 
  56.      * (由于我们的密码校验交给Shiro的SimpleAuthenticationInfo进行处理了 
  57.      *  所以我们需要修改下doGetAuthenticationInfo中的代码; 
  58.      * ) 
  59.      * @return 
  60.      */  
  61.     @Bean  
  62.     public HashedCredentialsMatcher hashedCredentialsMatcher(){  
  63.         HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();  
  64.           
  65.         hashedCredentialsMatcher.setHashAlgorithmName("md5");//散列算法:这里使用MD5算法;  
  66.         hashedCredentialsMatcher.setHashIterations(2);//散列的次数,比如散列2次,相当于 md5(md5(""));  
  67.           
  68.         return hashedCredentialsMatcher;  
  69.     }  
  70.       
  71.     @Bean    
  72.     public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor() {    
  73.         AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();    
  74.         advisor.setSecurityManager(securityManager());    
  75.         return advisor;    
  76.     }    
  77. }  

该配置类实际相当于spring-shiro.xml中的配置,参考:

xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd

      http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">
    <description>Shiro的配置description>

    <import resource="classpath:spring_shiro_redis.xml"/>

    <aop:aspectj-autoproxy/>

    <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
        
        <property name="realm" ref="authRealm"/>
        
        <property name="cacheManager" ref="shiroSpringCacheManager"/>


    bean>


    
    <bean id="authRealm" class="com.hlink.DC.shiro.CustomRealm">

        
        
        <property name="credentialsMatcher" ref="passwordMatcher"/>

        <property name="cacheManager" ref="shiroSpringCacheManager"/>
        
        <property name="cachingEnabled" value="true"/>
        
        <property name="authenticationCachingEnabled" value="true"/>
        
        <property name="authorizationCachingEnabled" value="true"/>
        
        <property name="authenticationCacheName" value="authenticationCache"/>
        
        <property name="authorizationCacheName" value="authorizationCache"/>

    bean>

    
    <bean id="shiroSpringCacheManager" class="com.hlink.DC.shiro.ShiroSpringCacheManager">
        <property name="redisTemplate" ref="redisTemplate">property>
    bean>

    
    <bean id="passwordMatcher" class="com.hlink.DC.shiro.CustomCredentialsMatcher">
        <property name="cacheManager" ref="redisCacheManager"/>
        <property name="expire_minute" value="30"/>
        <property name="passwordHash" ref="passwordHash"/>

    bean>
    
    <bean id="passwordHash" class="com.hlink.DC.shiro.Encrypt">
        
        <property name="hashAlgorithm" value="md5"/>
        
        <property name="hashIterations" value="5"/>
    bean>

    
    <bean id="rememberMeCookie" class="org.apache.shiro.web.servlet.SimpleCookie">
        
        <constructor-arg value="rememberMe"/>
        <property name="httpOnly" value="true"/>
        
        <property name="maxAge" value="604800"/>
    bean>

    
    <bean id="sessionIdCookie" class="org.apache.shiro.web.servlet.SimpleCookie">
        
        <constructor-arg value="sessionIdCookie"/>
        <property name="httpOnly" value="true"/>
        
        <property name="maxAge" value="1800"/>
    bean>

    
    <bean id="rememberMeManager" class="org.apache.shiro.web.mgt.CookieRememberMeManager">
        <property name="cipherKey"
                  value="#{T(org.apache.shiro.codec.Base64).decode('5aaC5qKm5oqA5pyvAAAAAA==')}"/>
        <property name="cookie" ref="rememberMeCookie"/>
    bean>
    
    <bean id="sessionManager"
          class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager">
        
        <property name="globalSessionTimeout" value="1800000"/>
        
        <property name="sessionIdUrlRewritingEnabled" value="true"/>
        <property name="sessionIdCookie" ref="sessionIdCookie"/>
        <property name="sessionDAO" ref="sessionDAO"/>
    bean>

    
    <bean id="sessionDAO"
          class="org.apache.shiro.session.mgt.eis.EnterpriseCacheSessionDAO">
        
        <property name="activeSessionsCacheName" value="activeSessionCache"/>
        <property name="cacheManager" ref="shiroSpringCacheManager"/>
    bean>

    
    <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
        
        <property name="securityManager" ref="securityManager"/>
        
        <property name="loginUrl" value="/login.jsp"/>
        
        <property name="successUrl" value="/index.jsp"/>
        
        <property name="unauthorizedUrl" value="/unauth.jsp"/>
        <property name="filterChainDefinitions">
            <value>
                
                /store/admin/session = anon
                /store/admin/logout = anon
                /store/admin/login = anon
                /web/validationCode/** = anon
                /store/admin= anon
                /** = anon
            value>
        property>
    bean>

    
    <bean
            class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
        <property name="staticMethod"
                  value="org.apache.shiro.SecurityUtils.setSecurityManager"/>
        <property name="arguments" ref="securityManager"/>
    bean>
    
    <bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>

    <bean id="exceptionHandler" class="com.hlink.DC.shiro.GlobalExceptionHandler" />

beans>


shiro的缓存中本人使用spring-data-redis集成Redis集群来做,具体配置:

spring-shiro-redis.xml

xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xmlns:c="http://www.springframework.org/schema/c"
      xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
   <description>spring-redis-cache配置文件description>

   
   
   
   <bean id="redisClusterConfiguration"
      class="org.springframework.data.redis.connection.RedisClusterConfiguration">
      <property name="maxRedirects" value="3">property>
      
      <property name="clusterNodes">
         <set>
            <bean class="org.springframework.data.redis.connection.RedisClusterNode">
               <constructor-arg name="host" value="192.168.50.241">constructor-arg>
               <constructor-arg name="port" value="6000">constructor-arg>
            bean>
            <bean class="org.springframework.data.redis.connection.RedisClusterNode">
               <constructor-arg name="host" value="192.168.50.241">constructor-arg>
               <constructor-arg name="port" value="6001">constructor-arg>
            bean>
            <bean class="org.springframework.data.redis.connection.RedisClusterNode">
               <constructor-arg name="host" value="192.168.50.241">constructor-arg>
               <constructor-arg name="port" value="6002">constructor-arg>
            bean>
            <bean class="org.springframework.data.redis.connection.RedisClusterNode">
               <constructor-arg name="host" value="192.168.50.239 ">constructor-arg>
               <constructor-arg name="port" value="7000">constructor-arg>
            bean>
            <bean class="org.springframework.data.redis.connection.RedisClusterNode">
               <constructor-arg name="host" value="192.168.50.239">constructor-arg>
               <constructor-arg name="port" value="7001">constructor-arg>
            bean>
            <bean class="org.springframework.data.redis.connection.RedisClusterNode">
               <constructor-arg name="host" value="192.168.50.239">constructor-arg>
               <constructor-arg name="port" value="7002">constructor-arg>
            bean>
         set>
      property>
   bean>

   <bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">
      
      <property name="maxIdle" value="100" />
      <property name="maxTotal" value="600" />
      
      <property name="minIdle" value="20" />
      
      <property name="testOnBorrow" value="true" />
      
      <property name="testOnReturn" value="true" />
      
      <property name="testWhileIdle" value="true" />
   bean>

   <bean id="jeidsConnectionFactory"
      class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
      <constructor-arg ref="redisClusterConfiguration" />
      <constructor-arg ref="jedisPoolConfig" />
      <property name="password" value="hlink">property>
   bean>


   <bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate">
      <property name="connectionFactory" ref="jeidsConnectionFactory" />
      
      <property name="enableTransactionSupport" value="true" />
      
      
      
      
      <property name="keySerializer">
         <bean
            class="org.springframework.data.redis.serializer.JdkSerializationRedisSerializer">bean>
      property>
      <property name="valueSerializer">
         <bean
            class="org.springframework.data.redis.serializer.JdkSerializationRedisSerializer" />
      property>
      <property name="hashKeySerializer">
         <bean
            class="org.springframework.data.redis.serializer.JdkSerializationRedisSerializer" />
      property>
      <property name="hashValueSerializer">
         <bean
            class="org.springframework.data.redis.serializer.JdkSerializationRedisSerializer" />
      property>
   bean>


<bean id="redisCacheManager" class="org.springframework.data.redis.cache.RedisCacheManager"
        factory-method="create" c:connection-factory-ref="jeidsConnectionFactory" />
beans>


shiro中的几个关键组成部分: 加密器+自定义密码比较器+全局异常拦截处理器+shiro缓存管理器,这几部分在上述的shiro集成配置中必须存在,如下:

加密器:

package com.hlink.DC.shiro;


import org.apache.shiro.crypto.hash.Md5Hash;

public class Encrypt {
   private int hashIterations = 2;
   private String hashAlgorithm;

   public int getHashIterations() {
      return hashIterations;
   }

   public void setHashIterations(int hashIterations) {
      this.hashIterations = hashIterations;
   }

   public String getHashAlgorithm() {
      return hashAlgorithm;
   }

   public void setHashAlgorithm(String hashAlgorithm) {
      this.hashAlgorithm = hashAlgorithm;
   }
   /*
    * 散列算法一般用于生成数据的摘要信息,是一种不可逆的算法,一般适合存储密码之类的数据,
    * 常见的散列算法如MD5SHA等。一般进行散列时最好提供一个salt(盐),比如加密密码“admin”
    * 产生的散列值是“21232f297a57a5a743894a0e4a801fc3”, 可以到一些md5解密网站很容易的通过散列值得到密码“admin”
    * 即如果直接对密码进行散列相对来说破解更容易,此时我们可以加一些只有系统知道的干扰数据,
    * 如用户名和ID(即盐);这样散列的对象是密码+用户名+ID”,这样生成的散列值相对来说更难破解。
    */

   // 高强度加密算法,不可逆

   public String md5(String password, String salt) {
      return new Md5Hash(password, salt, hashIterations).toString();
   }
   public String sha(String password, String salt) {
      return null;
   }
   public String hmac(String password, String salt) {
      return null;
   }
   public String encrypt(String password, String salt) {

      if (hashAlgorithm != null && hashAlgorithm.equals("md5") || hashAlgorithm.equals("MD5")) {
         return md5(password, salt);
      } /* else if(hashAlgorithm.equals("sha") || hashAlgorithm.equals("SHA")){
         return null;
      } else if(hashAlgorithm.equals("hmac") || hashAlgorithm.equals("HMAC")){
         return null;
      }*/ else{
         return null;
      }

   }

}

自定义密码比较器:

package com.hlink.DC.shiro;

import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authc.credential.SimpleCredentialsMatcher;

public class CustomCredentialsMatcher extends SimpleCredentialsMatcher {
   private org.springframework.cache.CacheManager cacheManager;
   private int expire_minute = 30;
   private Encrypt passwordHash;

   public Encrypt getPasswordHash() {
      return passwordHash;
   }

   public void setPasswordHash(Encrypt passwordHash) {
      this.passwordHash = passwordHash;
   }

   public int getExpire_minute() {
      return expire_minute;
   }

   public void setExpire_minute(int expire_minute) {
      this.expire_minute = expire_minute;
   }

   public org.springframework.cache.CacheManager getCacheManager() {
      return cacheManager;
   }

   public void setCacheManager(org.springframework.cache.CacheManager cacheManager) {
      this.cacheManager = cacheManager;
   }

   public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {
      UsernamePasswordToken usertoken = (UsernamePasswordToken) token;
      // 注意token.getPassword()拿到的是一个char[],不能直接用toString(),它底层实现不是我们想的直接字符串,只能强转
      Object tokenCredentials = passwordHash.encrypt(String.valueOf(usertoken.getPassword()),
            usertoken.getUsername());
      Object accountCredentials = getCredentials(info);
      // 将密码加密与系统加密后的密码校验,内容一致就返回true,不一致就返回false
      System.out.println("用户输入密码:"+tokenCredentials);
      System.out.println("系統保存密碼: "+getCredentials(info));
      return equals(tokenCredentials, accountCredentials);
   }

}


全局异常拦截:

package com.hlink.DC.shiro;

import net.sf.json.JSONObject;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.UnknownAccountException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@ControllerAdvice
public class GlobalExceptionHandler implements HandlerExceptionResolver {


    @Override
    public ModelAndView resolveException(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) {
        JSONObject js = new JSONObject();

        if (e instanceof UnknownAccountException) {
            js.put("code", "4006");
            js.put("msg", "用户名不存在·");
        } else if (e instanceof IncorrectCredentialsException) {
            js.put("code", "4006");
            js.put("msg", "用户名或密码错误");
        } else if (e instanceof NullPointerException) {
            js.put("code", "4006");
            js.put("msg", "系統空指針異常");
        } else if (e instanceof ClassCastException) {
            js.put("code", "4006");
            js.put("msg", "系统类型转换异常");
        }  else{
            js.put("code", "404");
            js.put("msg", e.getMessage());
        }
        ModelAndView mv = new ModelAndView();
        mv.addObject(js);
        return mv;
    }

    @ExceptionHandler(value = Exception.class)
    @ResponseBody// 返回json数据
    public JSONObject jsonErrorHandler(HttpServletRequest req, Exception e){
        JSONObject js = new JSONObject();

        if (e instanceof UnknownAccountException) {
            js.put("code", "4006");
            js.put("msg", "用户名不存在·");
        } else if (e instanceof IncorrectCredentialsException) {
            js.put("code", "4006");
            js.put("msg", "用户名密码错误");
        } else if (e instanceof NullPointerException) {
            js.put("code", "4006");
            js.put("msg", "空指針異常");
        } else if (e instanceof ClassCastException) {
            js.put("code", "4006");
            js.put("msg", "类型转换异常");
        }  else{
            js.put("code", "404");
            js.put("msg", e.getMessage());
        }

        return js;
    }
}

shiro缓存管理器:


package com.hlink.DC.shiro;

import com.hlink.DC_DB.model.User;
import org.apache.shiro.cache.Cache;
import org.apache.shiro.cache.CacheException;
import org.apache.shiro.cache.CacheManager;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.Destroyable;
import org.springframework.data.redis.core.BoundHashOperations;
import org.springframework.data.redis.core.RedisTemplate;

import java.util.Collection;
import java.util.Set;

/**
 * 

* 自定义cacheManage 扩张shiro里面的缓存 使用reids作缓存 *

* 引入自己定义的CacheManager 关于CacheManager的配置文件在spring-redis-cache.xml * * * @author xxxx * @date 201823 * @time 14:01:53 */ public class ShiroSpringCacheManager implements CacheManager, Destroyable { private String cacheKeyPrefix = "shiro_login:"; private RedisTemplate<String, Object> redisTemplate; public String getCacheKeyPrefix() { return cacheKeyPrefix; } public void setCacheKeyPrefix(String cacheKeyPrefix) { this.cacheKeyPrefix = cacheKeyPrefix; } public RedisTemplate<String, Object> getRedisTemplate() { return redisTemplate; } public void setRedisTemplate(RedisTemplate<String, Object> redisTemplate) { this.redisTemplate = redisTemplate; } @Override public <K, V> Cache<K, V> getCache(String name) throws CacheException { return new ShiroRedisCache<K, V>(cacheKeyPrefix + name); } @Override public void destroy() throws Exception { redisTemplate = null; } public class ShiroRedisCache<K, V> implements Cache<K, V> { private String cacheKey; public ShiroRedisCache(String cacheKey) { this.cacheKey = cacheKey; } @Override public V get(K key) throws CacheException { BoundHashOperations<String, K, V> hash = redisTemplate.boundHashOps(cacheKey); Object k = hashKey(key); return hash.get(k); } @SuppressWarnings("unchecked") @Override public V put(K key, V value) throws CacheException { BoundHashOperations<String, K, V> hash = redisTemplate.boundHashOps(cacheKey); Object k = hashKey(key); hash.put((K) k, value); return value; } @Override public V remove(K key) throws CacheException { BoundHashOperations<String, K, V> hash = redisTemplate.boundHashOps(cacheKey); Object k = hashKey(key); V value = hash.get(k); hash.delete(k); return value; } @Override public void clear() throws CacheException { redisTemplate.delete(cacheKey); } @Override public int size() { BoundHashOperations<String, K, V> hash = redisTemplate.boundHashOps(cacheKey); return hash.size().intValue(); } @Override public Set<K> keys() { BoundHashOperations<String, K, V> hash = redisTemplate.boundHashOps(cacheKey); return hash.keys(); } @Override public Collection<V> values() { BoundHashOperations<String, K, V> hash = redisTemplate.boundHashOps(cacheKey); return hash.values(); } protected Object hashKey(K key) { if (key instanceof PrincipalCollection) {// 此处很重要,如果key是登录凭证,那么这是访问用户的授权缓存;将登录凭证转为user对象,返回userid属性做为hash // key,否则会以user对象做为hash key,这样就不好清除指定用户的缓存了 PrincipalCollection pc = (PrincipalCollection) key; User user = (User) pc.getPrimaryPrincipal(); return user.getId(); } return key; } } }

最后在贴一下我的启动类:

package com.hlink.DC;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.ImportResource;
import org.springframework.transaction.annotation.EnableTransactionManagement;

@EnableAutoConfiguration
@EnableTransactionManagement
@ComponentScan
@ImportResource(locations = {"classpath:applicationContext.xml", "classpath:spring_shiro_redis.xml", "classpath:spring-shiro.xml", "classpath:spring-dubbo.xml"})
public class DCApplication {
    public static void main(String[] args) {
        SpringApplication.run(DCApplication.class, args);
    }

}

spring-boot对市场上常见的框架并不是完全集成的,例如dubbo等,但它的配置加载却具有相当的灵活性,开发者仍可以在开始接触的过渡期间使用自己的配置文件(applicationContext.xml,spring-shiro.xml.spring-dubbo.xml等)只需要在启动类中引入加载即可使用,但切记springboot已经完美的兼容了springmvc,所以不要再多余配置spring-mvc.xml中的所有配置

后言:以上内容均属个人感悟,如有错误欢迎指正,谢谢!!


你可能感兴趣的:(shiro缓存配置)