Apache Shiro集群要解决2个问题,一个是session的共享问题,一个是授权信息的cache共享问题,官网给的例子是Ehcache的实现,在配置说明上不算很详细,我这里用nosql(redis)替代了ehcache做了session和cache的存储。
shiro spring的默认配置(单机,非集群)
-
<span style="font-size:18px;"><span style="font-size:18px;"><bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
-
<property name="realm" ref="shiroDbRealm" />
-
<property name="cacheManager" ref="memoryConstrainedCacheManager" />
-
</bean>
-
-
-
<bean id="shiroDbRealm" class="com.xxx.security.shiro.custom.ShiroDbRealm">
-
<property name="credentialsMatcher" ref="customCredentialsMather"></property>
-
</bean>
-
-
<bean id="memoryConstrainedCacheManager" class="org.apache.shiro.cache.MemoryConstrainedCacheManager" />
-
-
-
<bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>
-
-
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
-
<property name="securityManager" ref="securityManager" />
-
<property name="loginUrl" value="/login" />
-
<property name="successUrl" value="/project" />
-
<property name="filterChainDefinitions">
-
<value>
-
/login = authc
-
/logout = logout
-
</value>
-
</property>
-
</bean> </span></span>
上面的配置是shiro非集群下的配置,DefaultWebSecurityManager类不需要注入sessionManager属性,它会使用默认的sessionManager类,请看源码
-
<span style="font-size:18px;"><span style="font-size:18px;"> public DefaultWebSecurityManager() {
-
super();
-
((DefaultSubjectDAO) this.subjectDAO).setSessionStorageEvaluator(new DefaultWebSessionStorageEvaluator());
-
this.sessionMode = HTTP_SESSION_MODE;
-
setSubjectFactory(new DefaultWebSubjectFactory());
-
setRememberMeManager(new CookieRememberMeManager());
-
setSessionManager(new ServletContainerSessionManager());
-
} </span></span>
在最后一行,set了默认的servlet容器实现的sessionManager,sessionManager会管理session的创建、删除等等。如果我们需要让session在集群中共享,就需要替换这个默认的sessionManager。在其官网上原话是这样的:
-
<span style="font-size:18px;"><span style="font-size:18px;"> Native Sessions
-
-
If you want your session configuration settings and clustering to be portable across servlet containers
-
(e.g. Jetty in testing, but Tomcat or JBoss in production), or you want to control specific session/clustering
-
features, you can enable Shiro's native session management.
-
-
The word 'Native' here means that Shiro's own enterprise session management implementation will be used to support
-
all Subject and HttpServletRequest sessions and bypass the servlet container completely. But rest assured - Shiro
-
implements the relevant parts of the Servlet specification directly so any existing web/http related code works as
-
expected and never needs to 'know' that Shiro is transparently managing sessions.
-
-
DefaultWebSessionManager
-
-
To enable native session management for your web application, you will need to configure a native web-capable
-
session manager to override the default servlet container-based one. You can do that by configuring an instance of
-
DefaultWebSessionManager on Shiro's SecurityManager. </span></span>
我们可以看到如果要用集群,就需要用本地会话,这里shiro给我准备了一个默认的native session manager,DefaultWebSessionManager,所以我们要修改spring配置文件,注入DefaultWebSessionManager
-
<span style="font-size:18px;"><span style="font-size:18px;"> <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
-
<property name="sessionManager" ref="defaultWebSessionManager" />
-
<property name="realm" ref="shiroDbRealm" />
-
<property name="cacheManager" ref="memoryConstrainedCacheManager" />
-
</bean>
-
<bean id="defaultWebSessionManager" class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager">
-
<property name="globalSessionTimeout" value="1200000" />
-
</bean>
我们继续看DefaultWebSessionManager的源码,发现其父类DefaultSessionManager中有sessionDAO属性,这个属性是真正实现了session储存的类,这个就是我们自己实现的redis session的储存类。
-
<span style="font-size:18px;"><span style="font-size:18px;"> protected SessionDAO sessionDAO;
-
-
private CacheManager cacheManager;
-
-
private boolean deleteInvalidSessions;
-
-
public DefaultSessionManager() {
-
this.deleteInvalidSessions = true;
-
this.sessionFactory = new SimpleSessionFactory();
-
this.sessionDAO = new MemorySessionDAO();
-
}
这里我们看到了,如果不自己注入sessionDAO,defaultWebSessionManager会使用MemorySessionDAO做为默认实现类,这个肯定不是我们想要的,所以这就自己动手实现sessionDAO吧。
我们自定义RedisSessionDAO继承AbstractSessionDAO,实现对session操作的方法,可以用redis、mongoDB等进行实现。
这个是自己redis的RedisCacheManager存储实现类:
-
<span style="font-size:18px;"><span style="font-size:18px;">package com.tgb.itoo.authority.cache;
-
-
import java.util.concurrent.ConcurrentHashMap;
-
import java.util.concurrent.ConcurrentMap;
-
-
import org.apache.shiro.cache.Cache;
-
import org.apache.shiro.cache.CacheException;
-
import org.apache.shiro.cache.CacheManager;
-
import org.slf4j.Logger;
-
import org.slf4j.LoggerFactory;
-
-
public class RedisCacheManager implements CacheManager{
-
-
private static final Logger logger = LoggerFactory
-
.getLogger(RedisCacheManager.class);
-
-
-
private final ConcurrentMap<String, Cache> caches = new ConcurrentHashMap<String, Cache>();
-
-
private RedisManager redisManager;
-
-
-
-
-
private String keyPrefix = "shiro_redis_cache:";
-
-
-
-
-
-
-
public String getKeyPrefix() {
-
return keyPrefix;
-
}
-
-
-
-
-
-
-
public void setKeyPrefix(String keyPrefix) {
-
this.keyPrefix = keyPrefix;
-
}
-
-
@Override
-
public <K, V> Cache<K, V> getCache(String name) throws CacheException {
-
logger.debug("获取名称为: " + name + " 的RedisCache实例");
-
-
Cache c = caches.get(name);
-
-
if (c == null) {
-
-
-
redisManager.init();
-
-
-
c = new RedisCache<K, V>(redisManager, keyPrefix);
-
-
-
caches.put(name, c);
-
}
-
return c;
-
}
-
-
public RedisManager getRedisManager() {
-
return redisManager;
-
}
-
-
public void setRedisManager(RedisManager redisManager) {
-
this.redisManager = redisManager;
-
}
-
}
-
这样RedisSessionDAO我们就完成了,下面继续修改我们spring配置文件:
这样第一个问题,session的共享问题我们就解决好了,下一篇介绍另一个问题,cache的共享问题。
来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/9399028/viewspace-2102815/
,如需转载,请注明出处,否则将追究法律责任。