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)中。