Apache shiro集群实现 (一) shiro入门介绍
Apache shiro集群实现 (二) shiro 的INI配置
Apache shiro集群实现 (三)shiro身份认证(Shiro Authentication)
Apache shiro集群实现 (四)shiro授权(Authentication)--访问控制
Apache shiro集群实现 (五)分布式集群系统下的高可用session解决方案
Apache shiro集群实现 (六)分布式集群系统下的高可用session解决方案---Session共享
Apache Shiro的基本配置和构成这里就不详细说明了,其官网有说明文档,这里仅仅说明集群的解决方案,详细配置:shiro web config
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>
-
- <!-- 自定义Realm -->
- <bean id="shiroDbRealm" class="com.xxx.security.shiro.custom.ShiroDbRealm">
- <property name="credentialsMatcher" ref="customCredentialsMather"></property>
- </bean>
- <!-- 用户授权信息Cache(本机内存实现) -->
- <bean id="memoryConstrainedCacheManager" class="org.apache.shiro.cache.MemoryConstrainedCacheManager" />
-
- <!-- 保证实现了Shiro内部lifecycle函数的bean执行 -->
- <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> </span></span>
我们继续看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();
- } </span></span>
这里我们看到了,如果不自己注入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);
-
- // fast lookup by name map
- private final ConcurrentMap<String, Cache> caches = new ConcurrentHashMap<String, Cache>();
-
- private RedisManager redisManager;
-
- /**
- * The Redis key prefix for caches
- */
- private String keyPrefix = "shiro_redis_cache:";
-
- /**
- * Returns the Redis session keys
- * prefix.
- * @return The prefix
- */
- public String getKeyPrefix() {
- return keyPrefix;
- }
-
- /**
- * Sets the Redis sessions key
- * prefix.
- * @param keyPrefix The prefix
- */
- 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) {
-
- // initialize the Redis manager instance
- redisManager.init();
-
- // create a new cache instance
- c = new RedisCache<K, V>(redisManager, keyPrefix);
-
- // add it to the cache collection
- caches.put(name, c);
- }
- return c;
- }
-
- public RedisManager getRedisManager() {
- return redisManager;
- }
-
- public void setRedisManager(RedisManager redisManager) {
- this.redisManager = redisManager;
- }
- }
- </span></span>
这样RedisSessionDAO我们就完成了,下面继续修改我们spring配置文件:
这样第一个问题,session的共享问题我们就解决好了,下一篇介绍另一个问题,cache的共享问题。
|