apache shiro集群实现(二)— cache共享

    上一篇已经解决了第一个问题,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();

}
下面我们自己实现redis的cacheManger
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


评论 22
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值