上一篇已经解决了第一个问题,session的共享,现在我们解决第二个问题cache的共享。
先看下spring的配置文件,上一篇已经提到过了
- <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager" depends-on="userRepository,roleRepository">
- <property name="sessionManager" ref="defaultWebSessionManager" />
- <property name="realm" ref="shiroDbRealm" />
- <property name="cacheManager" ref="memoryConstrainedCacheManager" />
- </bean>
- <bean id="memoryConstrainedCacheManager" class="org.apache.shiro.cache.MemoryConstrainedCacheManager" />
这里cacheManager我们注入了shiro自定的本机内存实现的cacheManager类,当然,这肯定不满足我们集群的需要,所以我们要自己实现cacheManager类,这里我还是用了redis作为cache的存储,先创建CustomShiroCacheManager实现类
- public class CustomShiroCacheManager implements CacheManager, Destroyable {
- private ShiroCacheManager shiroCacheManager;
- public ShiroCacheManager getShiroCacheManager() {
- return shiroCacheManager;
- }
- public void setShiroCacheManager(ShiroCacheManager shiroCacheManager) {
- this.shiroCacheManager = shiroCacheManager;
- }
- @Override
- public <K, V> Cache<K, V> getCache(String name) throws CacheException {
- return getShiroCacheManager().getCache(name);
- }
- @Override
- public void destroy() throws Exception {
- shiroCacheManager.destroy();
- }
- }
这里为了扩展,引入了ShiroCacheManager接口
- public interface ShiroCacheManager {
- <K, V> Cache<K, V> getCache(String name);
- void destroy();
- }
- public class JedisShiroCacheManager implements ShiroCacheManager {
- @Autowired
- private JedisCacheManager jedisCacheManager;
- @Override
- public <K, V> Cache<K, V> getCache(String name) {
- return new JedisShiroCache<K, V>(name, jedisCacheManager);
- }
- @Override
- public void destroy() {
- jedisCacheManager.getJedis().shutdown();
- }
- }
当然,这里仅仅是getCache,我第一次看源码时,也有这样的疑问,cache的add、remove等等方法在哪里实现呢?我们继续看看shiro的源码就会知道答案了
这个是自己relm的AuthorizingRealm中的方法
- protected AuthorizationInfo getAuthorizationInfo(PrincipalCollection principals) {
- if (principals == null) {
- return null;
- }
- AuthorizationInfo info = null;
- if (log.isTraceEnabled()) {
- log.trace("Retrieving AuthorizationInfo for principals [" + principals + "]");
- }
- Cache<Object, AuthorizationInfo> cache = getAvailableAuthorizationCache();
- if (cache != null) {
- if (log.isTraceEnabled()) {
- log.trace("Attempting to retrieve the AuthorizationInfo from cache.");
- }
- Object key = getAuthorizationCacheKey(principals);
- info = cache.get(key);
- if (log.isTraceEnabled()) {
- if (info == null) {
- log.trace("No AuthorizationInfo found in cache for principals [" + principals + "]");
- } else {
- log.trace("AuthorizationInfo found in cache for principals [" + principals + "]");
- }
- }
- }
- if (info == null) {
- // Call template method if the info was not found in a cache
- info = <strong>doGetAuthorizationInfo</strong>(principals);
- // If the info is not null and the cache has been created, then cache the authorization info.
- if (info != null && cache != null) {
- if (log.isTraceEnabled()) {
- log.trace("Caching authorization info for principals: [" + principals + "].");
- }
- Object key = getAuthorizationCacheKey(principals);
- <strong>cache.put</strong>(key, info);
- }
- }
- return info;
- }
如果大家去查查引入就知道我们自定义relm要实现一个叫做doGetAuthorizationInfo()的方法,它的作用是查询授权信息,我们注意加粗的2行,发现其实cache的put方法其实才是cache存储的核心类,其实现都在cache中,所以我们需要实现自己的cache,创建JedisShiroCache类
- public class JedisShiroCache<K, V> implements Cache<K, V> {
- private final String REDIS_SHIRO_CACHE = "shiro-cache:";
- private JedisManager jedisManager;
- private String name;
- public JedisShiroCache(String name, JedisManager jedisManager) {
- this.name = name;
- this.jedisManager = jedisManager;
- }
- /**
- * 自定义relm中的授权/认证的类名加上授权/认证英文名字
- * @return
- */
- public String getName() {
- if (name == null)
- return "";
- return name;
- }
- public void setName(String name) {
- this.name = name;
- }
- @Override
- public V get(K key) throws CacheException {
- byte[] byteKey = SerializeUtil.serialize(getCacheKey(key));
- byte[] byteValue = jedisManager.getValueByKey(byteKey);
- return (V) SerializeUtil.deserialize(byteValue);
- }
- @Override
- public V put(K key, V value) throws CacheException {
- V previos = get(key);
- jedisManager.saveValueByKey(SerializeUtil.serialize(getCacheKey(key)),
- SerializeUtil.serialize(value));
- return previos;
- }
- @Override
- public V remove(K key) throws CacheException {
- V previos = get(key);
- jedisManager.deleteByKey(SerializeUtil.serialize(getCacheKey(key)));
- return previos;
- }
- @Override
- public void clear() throws CacheException {
- byte[] keysPattern = SerializeUtil.serialize(this.REDIS_SHIRO_CACHE
- + "*");
- jedisManager.deleteByKeysPattern(keysPattern);
- }
- @Override
- public int size() {
- if (keys() == null)
- return 0;
- return keys().size();
- }
- @Override
- public Set<K> keys() {
- Set<byte[]> byteSet = jedisManager.getKeysByKeysPattern(SerializeUtil
- .serialize(this.REDIS_SHIRO_CACHE + "*"));
- Set<K> keys = new HashSet<K>();
- for (byte[] bs : byteSet) {
- keys.add((K) SerializeUtil.deserialize(bs));
- }
- return keys;
- }
- @Override
- public Collection<V> values() {
- Set<byte[]> byteSet = jedisManager.getKeysByKeysPattern(SerializeUtil
- .serialize(this.REDIS_SHIRO_CACHE + "*"));
- List<V> result = new LinkedList<V>();
- for (byte[] bs : byteSet) {
- result.add((V) SerializeUtil.deserialize(jedisManager
- .getValueByKey(bs)));
- }
- return result;
- }
- private String getCacheKey(Object key) {
- return this.REDIS_SHIRO_CACHE + getName() + ":" + key;
- }
最后修改spring配置文件
- <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager" depends-on="userRepository,roleRepository">
- <property name="sessionManager" ref="defaultWebSessionManager" />
- <property name="realm" ref="shiroDbRealm" />
- <property name="cacheManager" ref="customShiroCacheManager" />
- </bean>
- <bean id="customShiroCacheManager" class="com.nfschina.fourjoy.security.shiro.custom.cache.CustomShiroCacheManager">
- <property name="shiroCacheManager" ref="jedisShiroCacheManager" />
- </bean>
- <bean id="jedisShiroCacheManager" class="com.nfschina.fourjoy.security.shiro.custom.cache.JedisShiroCacheManager" />
这样就完成了整个shiro集群的配置
源码地址:https://github.com/michaelliuyang/shiro-Redis-cluster