Apache Shiro 会话集群
Apache Shiro的会话管理提供了一个非常令人兴奋的功能,你可通过Shiro本身实现会话集群而不需要担心容器的环境。你使用Shiro本身的会话和会话集群可以部署到Tomcat,Jetty,JBOSS,Geronimo等任何的环境下,而不用担心容器或环境对集群设置所需要的特定配置。Shiro的会话集群只需配置一次就可运行在任何环境下。
这是怎么做到的呢?
一切是基于Shiro的POJO体系结构,使得创建会话集群非常简单,只要在会话持久层实现集群机制。也就是说,如果你配置了一个具有集群功能的SessionDAO,这个DAO就可以与集群机制交互而SessionManager并不需要知道集群的细节。
分布式缓存
现在很多分布式缓存解决方案都已经解决了分布式数据保存在持久层的问题,像Ehcache+TerraCotta, GigaSpaces Oracle Coherence和 Memcached等等。因此在Shiro中创建会话缓存非常简单,只需配置好某个分布式缓存机制。
在实现一个分布式或企业级高速缓存时,对于集群会话存贮需要注意两点:
1、 有足够的内存保留所有的当前会话
2、 如果没有足够的内存来保留所有活动的会话,它必须支持磁盘溢出,确保会话不会丢失。
EnterpriseCacheSessionDAO
Shiro本身已经提供了一个SessionDAO的实现能保存企业级/分布式的缓存数据。这个实现就是EnterpriseCacheSessionDAO,可以在CacheManager中配置它来实现缓存机制。配置如下:
#This implementation would use your preferred distributed caching product's APIs:
activeSessionCache = my.org.apache.shiro.cache.CacheImplementation
sessionDAO = org.apache.shiro.session.mgt.eis.EnterpriseCacheSessionDAO
sessionDAO.activeSessionCache = $activeSessionCache
securityManager.sessionManager.sessionDAO = $sessionDAO
虽然象上面介绍的方法你可要直接往SessionDAO里注入一个Cache实例,但还可以用一种更常见的方式可以为Shiro中所有需要用到缓存的地方配置一个通用的CacheManager。在这种方式中你可以告诉EnterpriseCacheSessionDAO一个Cache的名字,而这个 Cache是用来存储活动的会话。
例如:
# This implementation would use your caching product's APIs:
cacheManager = my.org.apache.shiro.cache.CacheManagerImplementation
# Now configure the EnterpriseCacheSessionDAO and tell it what
# cache in the CacheManager should be used to store active sessions:
sessionDAO = org.apache.shiro.session.mgt.eis.EnterpriseCacheSessionDAO
# This is the default value. Change it if your CacheManager configured a different name:
sessionDAO.activeSessionsCacheName = shiro-activeSessionsCache
# Now have the native SessionManager use that DAO:
securityManager.sessionManager.sessionDAO = $sessionDAO
# Configure the above CacheManager on Shiro's SecurityManager
# to use it for all of Shiro's caching needs:
securityManager.cacheManager = $cacheManager
这种配置方法的特点是不论配置在什么地方,实际上是告诉SessionDAO实例都要使用Cache或CacheManager。那么SessionDAO是怎么使用分布式缓存呢?
当Shiro初始化SecurityManager时,它会检查SessionDAO是否实现了CacheManagerAware的接口。如果是的话,它会自动支持任何可用的全局配置的CacheManager。
当 Shiro读到securityManager.cacheManager = $ cacheManager这一行时,它会发现EnterpriseCacheSessionDAO实现了CacheManagerAware的接口并且调用setCacheManager方法,将你配置的CacheManager作为方法参数。
然后在运行时,当 EnterpriseCacheSessionDAO需要activeSessionsCache的时候将要求CacheManager实例化并返回它,使用activeSessionsCacheName做为查询的关键字来得到缓存实例。该高速缓存实例将被用于存储和检索会话所有的SessionDAO CRUD操作。
使用Ehcache + Terracotta
在Shiro中使用Enchache+Terracotta提供分布式缓存解决方案已相当成熟。在这个解决方案中象上面描述的Enchache会话缓存配置将不能正常工作,需要做一些Terracotta配置。配置信息要保存到一个enchance.xml文件,例如:
<ehcache>
<terracottaConfig url="localhost:9510"/>
<diskStore path="java.io.tmpdir/shiro-ehcache"/>
<defaultCache
maxElementsInMemory="10000"
eternal="false"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
overflowToDisk="false"
diskPersistent="false"
diskExpiryThreadIntervalSeconds="120">
<terracotta/>
</defaultCache>
<cache name="shiro-activeSessionCache"
maxElementsInMemory="10000"
eternal="true"
timeToLiveSeconds="0"
timeToIdleSeconds="0"
diskPersistent="false"
overflowToDisk="false"
diskExpiryThreadIntervalSeconds="600">
<terracotta/>
</cache>
<!-- Add more cache entries as desired, for example,
Realm authc/authz caching: -->
</ehcache>
你需要修改<terracottaConfig url="localhost:9510"/>的内容,写入相应的Terracotta服务器阵列的主机/端口。另请注意, activeSessionCache元素的diskPersistent或overflowToDisk属性都应该是false的,在群集配置中不支持 true。
除此之外还需要在Shiro中进行配置:
sessionDAO = org.apache.shiro.session.mgt.eis.EnterpriseCacheSessionDAO
# This name matches a cache name in ehcache.xml:
sessionDAO.activeSessionsCacheName = shiro-activeSessionsCache
securityManager.sessionManager.sessionDAO = $sessionDAO
# Configure The EhCacheManager:
cacheManager = org.apache.shiro.cache.ehcache.EhCacheManager
cacheManager.cacheManagerConfigFile = classpath:ehcache.xml
# Configure the above CacheManager on Shiro's SecurityManager
# to use it for all of Shiro's caching needs:
securityManager.cacheManager = $cacheManager
请记住,顺序很重要。通过在SecurityManager最后配置CacheManager,我们能确保CacheManager可以传播到所有先前配置的CacheManagerAware组件(如EnterpriseCachingSessionDAO)中。