Shiro 提供的 Cache 接口:
public interface Cache<K, V> {
//根据Key获取缓存中的值
public V get(K key) throws CacheException;
//往缓存中放入key-value,返回缓存中之前的值
public V put(K key, V value) throws CacheException;
//移除缓存中key对应的值,返回该值
public V remove(K key) throws CacheException;
//清空整个缓存
public void clear() throws CacheException;
//返回缓存大小
public int size();
//获取缓存中所有的key
public Set<K> keys();
//获取缓存中所有的value
public Collection<V> values();
}
Shiro 提供的 CacheManager 接口:
public interface CacheManager {
//根据缓存名字获取一个Cache
public <K, V> Cache<K, V> getCache(String name) throws CacheException;
}
Shiro 还提供了 CacheManagerAware 用于注入 CacheManager:
public interface CacheManagerAware {
//注入CacheManager
void setCacheManager(CacheManager cacheManager);
}
Shiro 内部相应的组件(DefaultSecurityManager)会自动检测相应的对象(如 Realm)是否实现了 CacheManagerAware 并自动注入相应的 CacheManager。
Realm 缓存
Shiro 提供了 CachingRealm,其实现了 CacheManagerAware 接口,提供了缓存的一些基础实现;另外 AuthenticatingRealm 及 AuthorizingRealm 分别提供了对 AuthenticationInfo 和 AuthorizationInfo 信息的缓存。
spring-shiro.xml配置
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<!-- 设置安全管理器的安全数据源为自定义的 Realm -->
<property name="realm" ref="myRealm"/>
<property name="cacheManager" ref="cacheManager"/>
...
</bean>
<!-- 配置缓存管理器 -->
<bean id="cacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager">
<property name="cacheManagerConfigFile" value="classpath:ehcache-shiro.xml"/>
</bean>
<bean id="myRealm" class="com.zm.web_shiro.realm.CustomRealm">
<!-- 配置缓存相关 -->
<!-- 启用缓存 -->
<property name="cachingEnabled" value="true"/>
<!-- 开启认证缓存-->
<property name="authenticationCachingEnabled" value="true"/>
<!-- 指定认证缓存的名字(与 ehcache.xml 中声明的相同) -->
<property name="authenticationCacheName" value="authenticationCache"/>
<!--开启授权缓存-->
<property name="authorizationCachingEnabled" value="true"/>
<!-- 指定授权缓存的名字(与 ehcache.xml 中声明的相同) -->
<property name="authorizationCacheName" value="authorizationCache"/>
</bean>
ehcache-shiro.xml
<ehcache>
<!--diskStore:缓存数据持久化的目录 地址 -->
<diskStore path="E:\cache\ehcache" />
<!--<diskStore path="java.io.tmpdir"/>--> <!-- 系统的默认临时文件路径 -->
<!--
name:缓存名称。
maxElementsInMemory:缓存最大数目
maxElementsOnDisk:硬盘最大缓存个数。
eternal:对象是否永久有效,一但设置了,timeout将不起作用。
overflowToDisk:是否保存到磁盘,当系统当机时
timeToIdleSeconds:设置对象在失效前的允许闲置时间(单位:秒)。仅当eternal=false对象不是永久有效时使用,可选属性,默认值是0,也就是可闲置时间无穷大。
timeToLiveSeconds:设置对象在失效前允许存活时间(单位:秒)。最大时间介于创建时间和失效时间之间。仅当eternal=false对象不是永久有效时使用,默认是0.,也就是对象存活时间无穷大。
diskPersistent:是否缓存虚拟机重启期数据 Whether the disk store persists between restarts of the Virtual Machine. The default value is false.
diskSpoolBufferSizeMB:这个参数设置DiskStore(磁盘缓存)的缓存区大小。默认是30MB。每个Cache都应该有自己的一个缓冲区。
diskExpiryThreadIntervalSeconds:磁盘失效线程运行时间间隔,默认是120秒。
memoryStoreEvictionPolicy:当达到maxElementsInMemory限制时,Ehcache将会根据指定的策略去清理内存。默认策略是LRU(最近最少使用)。你可以设置为FIFO(先进先出)或是LFU(较少使用)。
clearOnFlush:内存数量最大时是否清除。
memoryStoreEvictionPolicy:
Ehcache的三种清空策略;
FIFO,first in first out,这个是大家最熟的,先进先出。
LFU, Less Frequently Used,就是上面例子中使用的策略,直白一点就是讲一直以来最少被使用的。如上面所讲,缓存的元素有一个hit属性,hit值最小的将会被清出缓存。
LRU,Least Recently Used,最近最少使用的,缓存的元素有一个时间戳,当缓存容量满了,而又需要腾出地方来缓存新的元素的时候,那么现有缓存元素中时间戳离当前时间最远的元素将被清出缓存。
-->
<defaultCache
maxElementsInMemory="10000"
maxElementsOnDisk="10000000"
eternal="false"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
overflowToDisk="false"
diskPersistent="false"
diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU"
/>
<!-- 登录记录缓存 锁定10分钟 -->
<cache name="passwordRetryCache"
maxEntriesLocalHeap="2000"
eternal="false"
timeToIdleSeconds="3600"
timeToLiveSeconds="0"
overflowToDisk="false"
statistics="true">
</cache>
<!-- 并发登陆,管理会话缓存 -->
<cache name="shiro-activeSessionCache" eternal="false"
maxElementsInMemory="10000"
overflowToDisk="true"
timeToIdleSeconds="0"
timeToLiveSeconds="0"/>
<!-- 授权缓存 -->
<cache name="authenticationCache"
maxEntriesLocalHeap="2000"
eternal="false"
timeToIdleSeconds="3600"
overflowToDisk="false"
statistics="true">
</cache>
<!-- 认证缓存 -->
<cache name="authorizationCache"
maxEntriesLocalHeap="2000"
eternal="false"
timeToIdleSeconds="3600"
overflowToDisk="false"
statistics="true">
</cache>
</ehcache>
虽然我们在配置文件中设置了缓存能储存多久,但是当我们登出 subject.logout 时,认证和授权缓存全都是会被清除的,这里我重写了父类AuthorizingRealm 的清除方法,用来直观的学习。
@Override
protected void clearCachedAuthenticationInfo(PrincipalCollection principals) {
Cache c = getAuthenticationCache();
int cacheSize = c.keys().size();
log.info("清除【认证】缓存之前,【认证】缓存大小:" + cacheSize);
for(Object o : c.keys()){
log.info( o + " , " + c.get(o));
}
super.clearCachedAuthenticationInfo(principals);
log.info("调用父类清除【认证】缓存之后");
for(Object o : c.keys()){
log.info( o + " , " + c.get(o));
}
// 添加下面的代码清空【认证】的缓存
//User user = (User) principals.getPrimaryPrincipal();
String username = principals.getPrimaryPrincipal().toString();
SimplePrincipalCollection spc = new SimplePrincipalCollection(username,getName());
super.clearCachedAuthenticationInfo(spc);
log.info("添加了代码清除【认证】缓存之后");
cacheSize = c.keys().size();
log.info("【认证】缓存的大小:" + c.keys().size());
if (cacheSize == 0){
log.info("说明【认证】缓存被清空了。");
}
}
@Override
protected void clearCachedAuthorizationInfo(PrincipalCollection principals) {
Cache c = getAuthorizationCache();
int cacheSize = c.keys().size();
log.info("清除【授权】缓存之前,授权】缓存的大小:" + cacheSize);
for(Object o : c.keys()){
log.info( o + " , " + c.get(o));
}
super.clearCachedAuthorizationInfo(principals);
log.info("清除【授权】缓存之后");
cacheSize = c.keys().size();
log.info("【授权】缓存的大小:" + cacheSize);
for(Object o : c.keys()){
log.info( o + " , " + c.get(o));
}
if(cacheSize == 0){
log.info("说明【授权】缓存被清空了。");
}
}