Shiro缓存使用Redis、Ehcache、自带的MpCache实现的三种方式实例
第一种:使用Redis做缓存,将数据存储到redis数据库中
第一步:在项目里面引入redis,配置文件如下:
配置文件:spring_shiro_redis.xml
1 <?xml version="1.0" encoding="UTF-8"?> 2 <beans xmlns="http://www.springframework.org/schema/beans" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 xmlns:p="http://www.springframework.org/schema/p" 5 xmlns:c="http://www.springframework.org/schema/c" 6 xmlns:cache="http://www.springframework.org/schema/cache" 7 xsi:schemaLocation="http://www.springframework.org/schema/beans 8 http://www.springframework.org/schema/beans/spring-beans.xsd 9 http://www.springframework.org/schema/cache 10 http://www.springframework.org/schema/cache/spring-cache.xsd"> 11 <description>spring-redis-cache配置文件</description> 12 <!-- ###############################-Redis-########################################### --> 13 <!-- 配置redis和spring 的整合 --> 14 <bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig"> 15 <property name="maxTotal" value="${redis.maxTotal}" /> 16 <property name="maxIdle" value="${redis.maxIdle}" /> 17 <property name="maxWaitMillis" value="${redis.maxWaitMills}" /> 18 <!-- <property name="testOnBorrow" value="${redis.pool.testOnBorrow}" /> --> 19 </bean> 20 <bean id="jedisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory"> 21 <property name="hostName" value="${redis.masterHost}" /> 22 <property name="port" value="${redis.masterPort}" /> 23 <property name="timeout" value="${redis.timeout}" /> 24 <property name="password" value="${redis.masterPassword}" /> 25 <property name="poolConfig" ref="jedisPoolConfig" /> 26 </bean> 27 <bean id="template" class="org.springframework.data.redis.core.RedisTemplate"> 28 <property name="connectionFactory" ref="jedisConnectionFactory" /> 29 <!-- 开启事务 --> 30 <property name="enableTransactionSupport" value="true" /> 31 <!-- 序列化策略 推荐使用StringRedisSerializer --> 32 <!-- <property name="keySerializer"> 33 <bean class="org.springframework.data.redis.serializer.StringRedisSerializer"/> 34 </property> --> 35 <!-- <property name="valueSerializer"> 36 <bean class="org.springframework.data.redis.serializer.StringRedisSerializer"/> 37 </property> --> 38 <!--<property name="hashKeySerializer"> 39 <bean class="org.springframework.data.redis.serializer.StringRedisSerializer"/> 40 </property> 41 <property name="hashValueSerializer"> 42 <bean class="org.springframework.data.redis.serializer.StringRedisSerializer"/> 43 </property> --> 44 <property name="keySerializer"> 45 <bean class="org.springframework.data.redis.serializer.JdkSerializationRedisSerializer"></bean> 46 </property> 47 <property name="valueSerializer"> 48 <bean class="org.springframework.data.redis.serializer.JdkSerializationRedisSerializer"/> 49 </property> 50 <property name="hashKeySerializer"> 51 <bean class="org.springframework.data.redis.serializer.JdkSerializationRedisSerializer"/> 52 </property> 53 <property name="hashValueSerializer"> 54 <bean class="org.springframework.data.redis.serializer.JdkSerializationRedisSerializer"/> 55 </property> 56 </bean> 57 <!--spring cache--> 58 <bean id="cacheManager" class="org.springframework.data.redis.cache.RedisCacheManager" 59 c:redisOperations-ref="template"> 60 <!-- 默认缓存10分钟 --> 61 <property name="defaultExpiration" value="600"/> 62 <property name="usePrefix" value="true"/> 63 <!-- cacheName 缓存超时配置,半小时,一小时,一天 --> 64 <property name="expires"> 65 <map key-type="java.lang.String" value-type="java.lang.Long"> 66 <entry key="halfHour" value="1800"/> 67 <entry key="hour" value="3600"/> 68 <entry key="oneDay" value="86400"/> 69 <!-- shiro cache keys 对缓存的配置 --> 70 <entry key="authorizationCache" value="1800"/> 71 <entry key="authenticationCache" value="1800"/> 72 <entry key="activeSessionCache" value="1800"/> 73 </map> 74 </property> 75 </bean> 76 <!-- cache注解,和spring-ehcache.xml中的只能使用一个 --> 77 <cache:annotation-driven cache-manager="cacheManager" proxy-target-class="true"/> 78 </beans>
redis的配置文件redis.properties:
#Reids config #最大能够保持活动的对象数 redis.maxIdle=10 redis.maxTotal=100 #当池内没有返回对象时最大等待时间 redis.maxWaitMills=1000 #超时时间 redis.timeout=1000 #当调用borrow Object方法时,是否进行有效性检查 redis.pool.testOnBorrow=true #redis-master redis.masterHost=192.168.206.128 redis.masterPort=6379 redis.masterPassword=1234
下面是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" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd" default-lazy-init="true"> <description>Spring-shiro配置文件</description> <!--配置安全管理器--> <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager"> <!--设置自定义Realm--> <property name="realm" ref="myRealm"/> <!--将缓存管理器,交给安全管理器--> <property name="cacheManager" ref="shiroSpringCacheManager"/> <!-- 记住密码管理 --> <property name="rememberMeManager" ref="rememberMeManager"/> <property name="sessionManager" ref="sessionManager"/> </bean> <!-- 自定义realm --> <bean id = "myRealm" class="com.dzf.shiro.MyRealm"> <property name="cacheManager" ref="shiroSpringCacheManager"/> <property name="credentialsMatcher" ref="myCredentialsMatcher"/> <!-- 打开缓存 --> <property name="cachingEnabled" value="true"/> <!-- 打开身份认证缓存 --> <property name="authenticationCachingEnabled" value="true"/> <!-- 打开授权缓存 --> <property name="authorizationCachingEnabled" value="true"/> <!-- 缓存AuthenticationInfo信息的缓存名称 --> <property name="authenticationCacheName" value="authenticationCache"/> <!-- 缓存AuthorizationInfo信息的缓存名称 --> <property name="authorizationCacheName" value="authorizationCache"/> </bean> <!-- 配置自定义缓存管理器,中引入redis缓存管理器 --> <bean id = "shiroSpringCacheManager" class="com.dzf.shiro.ShiroSpringCacheManager"> <property name="cacheManager" ref="cacheManager"/> </bean> <!-- 密码错误5次锁定半小时 --> <bean id="myCredentialsMatcher" class="com.dzf.shiro.MyCredentialsMatcher"> <property name="cacheManager" ref="shiroSpringCacheManager"/> <property name="limitCacheName" value="halfHour"/> <property name="passwordHash" ref="passwordHash"/> </bean> <bean id = "passwordHash" class="com.dzf.shiro.PasswordHash"> <!-- 使用MD5 --> <property name="hashAlgorithm" value="md5" /> <!-- 加密5次 --> <property name="hashIterations" value="2"/> </bean> <!-- 记住密码Cookie --> <bean id="rememberMeCookie" class="org.apache.shiro.web.servlet.SimpleCookie"> <!-- cookie的名字 --> <constructor-arg value="rememberMe"/> <property name="httpOnly" value="true"/> <!-- 7天,--> <property name="maxAge" value="604800"/> </bean> <!-- sesisonCookie 设置 --> <bean id="sessionIdCookie" class="org.apache.shiro.web.servlet.SimpleCookie"> <!-- cookie的名字 --> <constructor-arg value="sessionIdCookie"/> <property name="httpOnly" value="true"/> <!-- 30分钟 单位是秒--> <property name="maxAge" value="1800"/> </bean> <!-- rememberMe管理器,cipherKey生成见{@code Base64Test.java} --> <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"/> <!-- url上带sessionId 默认为true --> <property name="sessionIdUrlRewritingEnabled" value="false"/> <property name="sessionIdCookie" ref="sessionIdCookie"/> <property name="sessionDAO" ref="sessionDAO"/> </bean> <!-- 会话DAO 用于会话的CRUD --> <bean id="sessionDAO" class="org.apache.shiro.session.mgt.eis.EnterpriseCacheSessionDAO"> <!-- Session缓存名字,配置30分钟过期 --> <property name="activeSessionsCacheName" value="activeSessionCache"/> <property name="cacheManager" ref="shiroSpringCacheManager"/> </bean> <!-- Shiro Filter --> <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean"> <!-- 安全管理器 --> <property name="securityManager" ref="securityManager"/> <!-- 默认的登陆访问url --> <property name="loginUrl" value="/login.jsp"/> <!-- 登陆成功后跳转的url --> <!-- <property name="successUrl" value="/index.jsp"/> --> <!-- 没有权限跳转的url --> <property name="unauthorizedUrl" value="/unauth.jsp"/> <property name="filterChainDefinitions"> <value> <!-- anon 不需要认证 authc 需要认证 user 验证通过或RememberMe登录的都可以 --> /commons/** = anon /static/** = anon /user/login = anon /user/toLogin= anon /user/register = anon /register.jsp = anon /** = authc </value> </property> </bean> <!-- 静态注入,相当于调用SecurityUtils.setSecurityManager(securityManager) --> <bean class="org.springframework.beans.factory.config.MethodInvokingFactoryBean"> <property name="staticMethod" value="org.apache.shiro.SecurityUtils.setSecurityManager"/> <property name="arguments" ref="securityManager"/> </bean> <!-- 保证实现了Shiro内部lifecycle函数的bean执行 --> <bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/> </beans>
第二步:定义自己的CacheManager
1 package com.dzf.shiro; 2 3 import org.apache.shiro.cache.Cache; 4 import org.apache.shiro.cache.CacheManager; 5 import org.apache.shiro.util.Destroyable; 6 /** 7 * <p> 自定义cacheManage 扩张shiro里面的缓存 使用reids作缓存 </p> 8 * <description> 9 * 引入自己定义的CacheManager 10 * 关于CacheManager的配置文件在spring-redis-cache.xml中 11 * </description> 12 * @author xxxx 13 * @date 2018年2月3日 14 * @time 14:01:53 15 */ 16 17 public class ShiroSpringCacheManager implements CacheManager ,Destroyable{ 18 19 private org.springframework.cache.CacheManager cacheManager; 20 21 public org.springframework.cache.CacheManager getCacheManager() { 22 return cacheManager; 23 } 24 25 public void setCacheManager(org.springframework.cache.CacheManager cacheManager) { 26 this.cacheManager = cacheManager; 27 } 28 29 @Override 30 public void destroy() throws Exception { 31 cacheManager = null; 32 } 33 34 @Override 35 public <K, V> Cache<K, V> getCache(String name) { 36 if (name == null ){ 37 return null; 38 } 39 return new ShiroSpringCache<K,V>(name,getCacheManager()); 40 } 41 42 43 }
定义自己实现的Cache,实现了Shiro包里的Cache
1 package com.dzf.shiro; 2 3 import org.apache.shiro.cache.CacheException; 4 import org.slf4j.Logger; 5 import org.slf4j.LoggerFactory; 6 import org.springframework.cache.Cache; 7 import org.springframework.cache.Cache.ValueWrapper; 8 import org.springframework.cache.CacheManager; 9 10 import java.util.Collection; 11 import java.util.Set; 12 13 /** 14 * <p> 自定义缓存 将数据存入到redis中 </p> 15 * @author xxx 16 * @date 2018年2月1日 17 * @time 22:32:11 18 * @param <K> 19 * @param <V> 20 */ 21 @SuppressWarnings("unchecked") 22 public class ShiroSpringCache<K,V> implements org.apache.shiro.cache.Cache<K, V>{ 23 private static final Logger log = LoggerFactory.getLogger(ShiroSpringCache.class); 24 private CacheManager cacheManager; 25 private Cache cache; 26 // private RedisCache cache2; 27 public ShiroSpringCache(String name, CacheManager cacheManager) { 28 if(name==null || cacheManager==null){ 29 throw new IllegalArgumentException("cacheManager or CacheName cannot be null."); 30 } 31 this.cacheManager = cacheManager; 32 //这里首先是从父类中获取这个cache,如果没有会创建一个redisCache,初始化这个redisCache的时候 33 //会设置它的过期时间如果没有配置过这个缓存的,那么默认的缓存时间是为0的,如果配置了,就会把配置的时间赋予给这个RedisCache 34 //如果从缓存的过期时间为0,就表示这个RedisCache不存在了,这个redisCache实现了spring中的cache 35 this.cache= cacheManager.getCache(name); 36 } 37 @Override 38 public V get(K key) throws CacheException { 39 log.info("从缓存中获取key为{}的缓存信息",key); 40 if(key == null){ 41 return null; 42 } 43 ValueWrapper valueWrapper = cache.get(key); 44 if(valueWrapper==null){ 46 return null; 47 } 48 return (V) valueWrapper.get(); 49 } 50 51 @Override 52 public V put(K key, V value) throws CacheException { 53 log.info("创建新的缓存,信息为:{}={}",key,value); 54 cache.put(key, value); 55 return get(key); 56 } 57 58 @Override 59 public V remove(K key) throws CacheException { 60 log.info("干掉key为{}的缓存",key); 61 V v = get(key); 62 cache.evict(key);//干掉这个名字为key的缓存 63 return v; 64 } 65 66 @Override 67 public void clear() throws CacheException { 68 log.info("清空所有的缓存"); 69 cache.clear(); 70 } 71 72 @Override 73 public int size() { 74 return cacheManager.getCacheNames().size(); 75 } 76 77 /** 78 * 获取缓存中所的key值 79 */ 80 @Override 81 public Set<K> keys() { 82 return (Set<K>) cacheManager.getCacheNames(); 83 } 84 85 /** 86 * 获取缓存中所有的values值 87 */ 88 @Override 89 public Collection<V> values() { 90 return (Collection<V>) cache.get(cacheManager.getCacheNames()).get(); 91 } 92 93 @Override 94 public String toString() { 95 return "ShiroSpringCache [cache=" + cache + "]"; 96 } 97 }
我来稍微解释下这个自定义ShiroSpringCache类中的CacheManager,这个是CacheManager其实就是RedisCacheManager,我们通过getter和setter注入过,RedisCacheManager是CacheManager的实现类.自己跟着源码看下去,一看就可以看的懂。
到此为止,使用redis做缓存,和spring的集成就完成了。注意:需要使用的缓存只需要在spring_shiro_redis.xml中配置就行了,放入缓存中的对象需要实现序列号接口
第二种:使用Ehcache做缓存,可以将数据存储到磁盘中,也可以存到内存中
同样需要配置文件:ehcache.xml
1 <?xml version="1.0" encoding="UTF-8"?> 2 <ehcache updateCheck="false" dynamicConfig="false"> 3 <diskStore path="java.io.tmpdir"/> 4 <!--授权信息缓存--> 5 <cache name="authorizationCache" 6 maxEntriesLocalHeap="2000" 7 eternal="false" 8 timeToIdleSeconds="1800" 9 timeToLiveSeconds="1800" 10 overflowToDisk="false" 11 statistics="true"> 12 </cache> 13 <!--身份信息缓存--> 14 <cache name="authenticationCache" 15 maxEntriesLocalHeap="2000" 16 eternal="false" 17 timeToIdleSeconds="1800" 18 timeToLiveSeconds="1800" 19 overflowToDisk="false" 20 statistics="true"> 21 </cache> 22 <!--session缓存--> 23 <cache name="activeSessionCache" 24 maxEntriesLocalHeap="2000" 25 eternal="false" 26 timeToIdleSeconds="1800" 27 timeToLiveSeconds="1800" 28 overflowToDisk="false" 29 statistics="true"> 30 </cache> 31 32 <!-- 缓存半小时 --> 33 <cache name="halfHour" 34 maxElementsInMemory="10000" 35 maxElementsOnDisk="100000" 36 eternal="false" 37 timeToIdleSeconds="1800" 38 timeToLiveSeconds="1800" 39 overflowToDisk="false" 40 diskPersistent="false" /> 41 42 <!-- 缓存一小时 --> 43 <cache name="hour" 44 maxElementsInMemory="10000" 45 maxElementsOnDisk="100000" 46 eternal="false" 47 timeToIdleSeconds="3600" 48 timeToLiveSeconds="3600" 49 overflowToDisk="false" 50 diskPersistent="false" /> 51 52 <!-- 缓存一天 --> 53 <cache name="oneDay" 54 maxElementsInMemory="10000" 55 maxElementsOnDisk="100000" 56 eternal="false" 57 timeToIdleSeconds="86400" 58 timeToLiveSeconds="86400" 59 overflowToDisk="false" 60 diskPersistent="false" /> 61 62 <!-- 63 name:缓存名称。 64 maxElementsInMemory:缓存最大个数。 65 eternal:对象是否永久有效,一但设置了,timeout将不起作用。 66 timeToIdleSeconds:设置对象在失效前的允许闲置时间(单位:秒)。仅当eternal=false对象不是永久有效时使用,可选属性,默认值是0,也就是可闲置时间无穷大。 67 timeToLiveSeconds:设置对象在失效前允许存活时间(单位:秒)。最大时间介于创建时间和失效时间之间。仅当eternal=false对象不是永久有效时使用,默认是0.,也就是对象存活时间无穷大。 68 overflowToDisk:当内存中对象数量达到maxElementsInMemory时,Ehcache将会对象写到磁盘中。 69 diskSpoolBufferSizeMB:这个参数设置DiskStore(磁盘缓存)的缓存区大小。默认是30MB。每个Cache都应该有自己的一个缓冲区。 70 maxElementsOnDisk:硬盘最大缓存个数。 71 diskPersistent:是否缓存虚拟机重启期数据 Whether the disk store persists between restarts of the Virtual Machine. The default value is false. 72 diskExpiryThreadIntervalSeconds:磁盘失效线程运行时间间隔,默认是120秒。 73 memoryStoreEvictionPolicy:当达到maxElementsInMemory限制时,Ehcache将会根据指定的策略去清理内存。默认策略是LRU(最近最少使用)。你可以设置为FIFO(先进先出)或是LFU(较少使用)。 74 clearOnFlush:内存数量最大时是否清除。 75 --> 76 <defaultCache name="defaultCache" 77 maxElementsInMemory="10000" 78 eternal="false" 79 timeToIdleSeconds="600" 80 timeToLiveSeconds="600" 81 overflowToDisk="false" 82 maxElementsOnDisk="100000" 83 diskPersistent="false" 84 diskExpiryThreadIntervalSeconds="120" 85 memoryStoreEvictionPolicy="LRU"/> 86 87 </ehcache>
spring和ehcache集成的配置文件:spring_ehcache.xml
1 <?xml version="1.0" encoding="UTF-8"?> 2 <beans xmlns="http://www.springframework.org/schema/beans" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 xmlns:cache="http://www.springframework.org/schema/cache" 5 xsi:schemaLocation="http://www.springframework.org/schema/beans 6 http://www.springframework.org/schema/beans/spring-beans.xsd 7 http://www.springframework.org/schema/cache 8 http://www.springframework.org/schema/cache/spring-cache.xsd"> 9 <!-- Spring提供的基于的Ehcache实现的缓存管理器 --> 10 <!-- 如果有多个ehcacheManager要在bean加上p:shared="true" --> 11 <bean id="ehcacheManager" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean" > 12 <property name="configLocation" value="classpath:ehcache.xml"/> 13 </bean> 14 <bean id="cacheManager" class="org.springframework.cache.ehcache.EhCacheCacheManager"> 15 <property name="cacheManager" ref="ehcacheManager"/> 16 <property name="transactionAware" value="true"/> 17 </bean> 18 <!-- cache注解,和spring-redis.xml中的只能使用一个 --> 19 <cache:annotation-driven cache-manager="cacheManager" proxy-target-class="true"/> 20 </beans>
从redis切到ehcache,除了引入上面这两个配置文件,其他的基本没有要动的,当然需要在spring.xml中,将spring-ehcache.xml引进去就像这样:
1 <!--redis 和 ehcache 任选其一 --> 2 <import resource="spring-ehcache.xml"/> 3 <!--<import resource="spring-redis-cache.xml"/>--> 4 <import resource="spring-shiro.xml"/>
我想熟悉spring的小伙伴,这都看的懂吧,
这样我们就实现了从redis切到ehcache,需要格外注意的是,命名的规范性,各缓存的名字,各bean的id保持一致,切换才方便。
第三种:使用MemoryConstrainedCacheManager这个缓存管理器,将数据缓存到内存中去
解释:Shiro已经为我们编写了一套缓存的实现,那就是MpCache,是使用Map实现的,有兴趣的可以去看源码
怎么使用这个shiro已经为我们实现好的缓存,是非常容易的。
在上面的spring-shiro.xml中,我标红的这一句:
1 <bean id = "shiroSpringCacheManager" class="com.dzf.shiro.ShiroSpringCacheManager"> 2 <property name="cacheManager" ref="cacheManager"/>
只需要把这个改为下面这句:
1 <!-- 配置shiro自带的缓存管理器 --> 2 <bean id = "shiroSpringCacheManager" class="org.apache.shiro.cache.MemoryConstrainedCacheManager"/>
就好了,对的,你没有看错,就是这么的简单。
好了,至此,三种实现都说完了,自己就自己的需求看,需要使用哪种级别的缓存吧。