内容简介:在本章我们就来讲讲如何将 Shiro 中的授权数据缓存到 Redis 中。Shiro 为授权数据的缓存提供了两个借口,一个是
在 手撸 Java Web RBAC 权限管理 中,我们自己实现了一个简易的 RBAC 权限管理框架,且我们也提到了一些缺陷,其中一点就是 : 每次请求需要授权的页面都会去数据库查询此用户对应的权限数据和角色数据,太耗费资源,应该进行缓存。
本章我们就来讲讲如何将 Shiro 中的授权数据缓存到 Redis 中。
API
Shiro 为授权数据的缓存提供了两个借口,一个是 CacheManager
,一个是 Cache
。
根据这两个接口,我们完全可以将授权数据缓存到任何地方,包括 redis
、 ehcache
、内存等。
Redis
既然我们要缓存到 Redis 中,我们需要搭建 Redis 环境,并导入 Redis 工具类:
<dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> <version>2.9.0</version> </dependency>
新建配置文件 spring-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" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="jedisPool" class="redis.clients.jedis.JedisPool"> <!-- Jedis 配置信息 --> <constructor-arg name="poolConfig" ref="jedisPoolConfig"/> <!-- Redis URL --> <constructor-arg name="host" value="127.0.0.1"/> <!-- Redis 端口--> <constructor-arg name="port" value="6379"/> <!-- Redis 密码 --> <!--<constructor-arg value=""/>--> </bean> <bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig"> <!-- 最大连接数 --> <property name="maxTotal" value="500"/> <!-- 最大闲置 --> <property name="maxIdle" value="100"/> <!-- 最小闲置 --> <property name="minIdle" value="10"/> <!-- 最大等待 --> <property name="maxWaitMillis" value="5000"/> <!-- 可以获取 --> <property name="testOnBorrow" value="true"/> </bean> </beans>
Cache
我们来创建一个 RedisCache
继承自 org.apache.shiro.cache.Cache
,来实现它的方法:
package im.zhaojun.cache; import im.zhaojun.util.JedisUtil; import org.apache.log4j.Logger; import org.apache.shiro.cache.Cache; import org.apache.shiro.cache.CacheException; import org.springframework.stereotype.Component; import org.springframework.util.SerializationUtils; import javax.annotation.Resource; import java.util.Collection; import java.util.Set; @Component public class RedisCache<K, V> implements Cache<K, V> { private static final Logger logger = Logger.getLogger(RedisCache.class); @Resource private JedisUtil jedisUtil; private final String CACHE_PREFIX = "shiro-cache:"; private byte[] getKeyBytes(K k) { return (CACHE_PREFIX + k).getBytes(); } @Override public V get(K k) throws CacheException { logger.info("从 Redis 中读取授权信息..."); byte[] key = getKeyBytes(k); byte[] value = jedisUtil.get(key); if (value != null) { return (V) SerializationUtils.deserialize(value); } return null; } @Override public V put(K k, V v) throws CacheException { byte[] key = getKeyBytes(k); byte[] value = SerializationUtils.serialize(v); jedisUtil.set(key, value); jedisUtil.expire(key, 600); return v; } @Override public V remove(K k) throws CacheException { byte[] key = getKeyBytes(k); byte[] value = jedisUtil.get(key); jedisUtil.del(key); if (value != null) { SerializationUtils.deserialize(value); } return null; } @Override public void clear() throws CacheException { jedisUtil.delKeysByPrefix(CACHE_PREFIX); } @Override public int size() { return jedisUtil.getKeysByPrefix(CACHE_PREFIX).size(); } @Override public Set<K> keys() { return (Set<K>) jedisUtil.getKeysByPrefix(CACHE_PREFIX); } @Override public Collection<V> values() { return jedisUtil.getValuesByPrefix(CACHE_PREFIX); } }
其中没什么难点,只是对 redis 的基本增删改查操作,由于是存储到 redis 中,所以我们为缓存数据的 key 添加了前缀,以便再次获取。
CacheManager
我们创建一个 RedisCacheManager
类来继承自 org.apache.shiro.cache.AbstractCacheManager
,当然你也可以直接继承自 org.apache.shiro.cacheCacheManager
:
package im.zhaojun.cache; import org.apache.shiro.cache.Cache; import org.apache.shiro.cache.CacheException; import org.apache.shiro.cache.AbstractCacheManager; import javax.annotation.Resource; @Component public class RedisCacheManager extends AbstractCacheManager { @Resource private RedisCache redisCache; @Override protected Cache createCache(String s) throws CacheException { return redisCache; } }
这里在 createCache()
方法中返回我们的自定义 RedisCache
对象即可。
Spring
然后我们将 RedisCacheManager
配置到 securityManager
中:
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager"> <property name="realm" ref="myRealm"/> <property name="cacheManager" ref="redisCacheManager"/> </bean>
以及将 spring-redis.xml
配置到 web.xml
中:
<context-param> <param-name>contextConfigLocation</param-name> <param-value> classpath:spring.xml, classpath:spring-shiro.xml, classpath:spring-redis.xml </param-value> </context-param>
Test
然后我们分别在 Realm
的 doGetAuthorizationInfo
方法和 RedisCache
的 get
方法中分别打印一条日志,看何时会访问数据库,何时会访问 Redis
缓存的数据。
-
首先是未认证的情况下,访问需要权限的的页面,不会输出任何信息,因为需要认证后,才会根据认证信息去获取授权现象,没有认证时,会直接拦截。
-
认证之后,访问需要授权的页面,会输入如下信息:
im.zhaojun.cache.RedisCache 15:09:14,015 INFO RedisCache:30 - 从 Redis 中读取授权信息... im.zhaojun.realm.MyRealm 15:09:14,016 INFO MyRealm:23 - 从数据库中读取授权信息...
由此可见,Shiro 会先去 Redis 中取数据,如果 Redis 中没有,再去 Realm(数据库) 中取。
然后再次访问这个页面,输入:
im.zhaojun.cache.RedisCache 15:11:13,351 INFO RedisCache:30 - 从 Redis 中读取授权信息...
因为缓存中已经有了,就不再去数据库中查询了。
小结
其实频繁从 Redis 中读取也是比较浪费资源的, Redis 的连接同样宝贵,最好的办法还是直接存储在内存中,但也是各有利弊,需要根据实际项目来决定使用哪种方案。
放到 Redis 的好处是:可以用来做跨项目/机器的数据缓存,可以集群,持久化等。
放到内存的好处是:速度快,使用方便快捷。
但使用这种缓存还有一个比较重要的事情,就是当数据库中的授权数据发生修改时,也要记得刷新缓存中的数据,不然会出现数据错乱,实现方式可以通过直接覆盖缓存,消息队列通知等方式,需要根据不同项目来选区不同方式,由于篇幅原因这里不再展开讲了。
本章代码地址 : https://github.com/zhaojun1998/Premission-Study/tree/master/Permission-Shiro-08/
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Clojure编程
Chas Emerick、Brian Carper、Christophe Grand / 徐明明、杨寿勋 / 电子工业出版社 / 2013-3-26 / 99.00元
Clojure是一种实用的通用语言,它是传奇语言LISP的方言,可与Ruby、Python等动态语言相媲美,更以无缝Java库、服务,以及拥有JVM系统得天独厚的资源优势而胜出。本书既可以用来熟悉Clojure基础知识与常见例子,也可了解其相关的实践领域与话题,更可以看到这一JVM平台上的LISP如何帮助消除不必要的复杂性,为大家在编程实践中解决最具挑战性的问题开辟新的选择——更具灵活性,更适于W......一起来看看 《Clojure编程》 这本书的介绍吧!