当权限信息存放在数据库中时,对于每次前端的访问请求都需要进行一次数据库查询。特别是在大量使用shiro的jsp标签的场景下,对应前端的一个页面访问请求会同时出现很多的权限查询操作,这对于权限信息变化不是很频繁的场景,每次前端页面访问都进行大量的权限数据库查询是非常不经济的。shiro eache缓存方案有效解决了这个问题,但是多台服务器操作的时候就会产生一个问题,一个用户多次登陆,负载均衡可能把它随机分给集群中的任何一个服务器,那么多次操作后,每台服务器都会缓存一个相同的角色权限,造成内存上的浪费.所以用redis去序列化角色权限,每次都去redis中查询操作,这样是一个有效合理的解决方案
一.shiro缓存机制
Shiro提供了类似于Spring的Cache抽象,即Shiro本身不实现Cache,但是对Cache进行了又抽象,方便更换不同的底层Cache实现。所以我们如果想要实现redis缓存处理,必须先实现两个抽象接口Cache和CacheManager
二.shiro抽象接口实现
(1)实现Cache参考代码
//定义继承Cache类,去在原先shiro处理缓存的基础上,让其把缓存的权限角色缓存到redis中
public class RedisCache<k,V> implements Cache<k,V> {
//RedisTemplate是Spring-date的一个核心操作类
private RedisTemplate<String,Object> redisTemplate;
//定义一个构造方法需要把redisTempate接收过来,在Shiro原先提供的缓存增删改查方案上
//利用redisTempate进行redis数据库储存
public RedisCache(RedisTemplate<String,Object> redisTemplate){
this.redisTemplate=redisTemplate;
}
//取
@Override
public V get(k k) throws CacheException {
System.out.println("get:key:"+k);
return (V)redisTemplate.opsForValue().get(k.toString());
}
//存
@Override
public V put(k k, V v) throws CacheException {
System.out.println("put:key:"+k+"Value:"+v);
redisTemplate.opsForValue().set(k.toString(),v);
return v;
}
//删
@Override
public V remove(k k) throws CacheException {
System.out.println("remove:k:"+k);
//定义一个方法取得要删除key的value
V v=this.get(k);
redisTemplate.delete(k.toString());
return v;
}
@Override
public void clear() throws CacheException {
System.out.println("*****flushdb****");
//OpsForValue是execute的封装版,execute可以进行类似原始jedis操作
redisTemplate.execute(new RedisCallback<Boolean>() {
@Override
public Boolean doInRedis(RedisConnection redisConnection) throws DataAccessException {
//清空数据库
redisConnection.flushDb();
return true;
}
});
}
//查询所有数据量
@Override
public int size() {
System.out.println("******size*****");
return redisTemplate.execute(new RedisCallback<Integer>() {
@Override
public Integer doInRedis(RedisConnection redisConnection) throws DataAccessException {
//keys返回的是集合,再跟上size方法,得到数据的数量
return redisConnection.keys("*".getBytes()).size();
}
});
}
//获得所有的key
@Override
public Set<k> keys() {
System.out.println("*******keys******");
return redisTemplate.execute(new RedisCallback<Set<k>>() {
@Override
public Set<k> doInRedis(RedisConnection redisConnection) throws DataAccessException {
Set<k> set=new HashSet<k>();
//key从redis数据库中取出以字节数组的形式返回
Set<byte[]> keys=redisConnection.keys("*".getBytes());
Iterator<byte[]> iter=keys.iterator();
while (iter.hasNext()){
set.add((k) iter.next());
}
return set;
}
});
}
//获得所有的Value
@Override
public Collection<V> values() {
System.out.println("*****keys******");
return redisTemplate.execute(new RedisCallback<Set<V>>() {
@Override
public Set<V> doInRedis(RedisConnection redisConnection) throws DataAccessException {
Set<V> set=new HashSet<V>();
//key从redis数据库中取出以字节数组的形式返回
Set<byte[]> keys=redisConnection.keys("*".getBytes());
Iterator<byte[]> iter=keys.iterator();
while (iter.hasNext()){
set.add((V)redisConnection.get(iter.next()));
}
return set;
};
});
}
}
(2)实现cacheManager接口
public class RedisCacheMange implements CacheManager {
private final ConcurrentMap<String,Cache> caches=new ConcurrentHashMap<String, Cache>();
private RedisTemplate<String,Object> redisTemplate;
//定义一个方法,让其从外部进行注入redisTemplate
public void setRedisTemplate(RedisTemplate<String,Object> redisTemplate){
this.redisTemplate=redisTemplate;
}
//CacheManger带的方法,获取cache
@Override
public <K, V> Cache<K, V> getCache(String s) throws CacheException {
//首先通过ConcurrentMap中取得(也就是从电脑缓存中取得)
Cache<K,V> cache=caches.get(s);
//如果从电脑中无法取得缓存数据,那就需要从redis数据库中取
if (cache==null){
//实例化自定义的RedisCache类,并讲注入的redisTemplate传入进去
//因为自定义的类继承了Shiro的CRUD所以可以自动从redis数据取
cache=new RedisCache(this.redisTemplate);
//如果能从redis数据库取出数据,还需要保存到当前服务器上,也就是保存在ConcurrentMap中
this.caches.put(s,cache);
}
return cache;
}
}
三.配置spring-shiro文件
bean实现接口类,且需要注入已经配置好的redisTemplate
<!--配置自定义redis缓存机制-->
<bean id="redisCacheMange" class="cn.travel.Cache.RedisCacheMange">
<property name="redisTemplate" ref="redisTemplate"/>
</bean>
在shiroSecurity中注入缓存机制
!--配置安全管理器-->
<bean id="scurityManage" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<!--配置你需要使用的Realms-->
<property name="realm" ref="empRealm"/>
<!--配置会话管理-->
<property name="sessionManager" ref="sessionManager"/>
<!--配置缓存管理-->
<property name="cacheManager" ref="redisCacheMange"/>
</bean>
四.测试
在项目登陆后会出现如下
实现类讲权限缓存入redis数据库
我们进入redis服务器查看这条数据
成功!!!