shiro的Cache機制

Shiro開發團隊明白在許多應用程序中性能是相當重要的。Caching 是Shiro 中的一個重要功能,以確保安全操做保持儘量的快。java

可是,Shiro並不實現緩存的功能,Shiro 的緩存支持基本上是一個抽象的(包裝)API,它將「坐」在一個基本的緩存機制產品(例 如,Ehcache,OSCache,Terracotta,Coherence,GigaSpaces,JBossCache 等)之上。這容許Shiro終端用戶配置他們喜歡的任何緩存機制。spring

Caching APIapache

Shiro 有三個重要的緩存接口:緩存

1:CacheManager - 負責全部緩存的主要管理組件,它返回Cache 實例安全

2:Cache - 維護key/value 對app

3:CacheManagerAware - 經過想要接收和使用CacheManager 實例的組件來實現框架

CacheManager 返回Cache 實例,各類不一樣的Shiro 組件使用這些Cache 實例來緩存必要的數據。任何實現了CacheManagerAware 的Shiro 組件將會自動地接收一個配置好的CacheManager,該CacheManager 可以用來獲取Cache 實例。性能

Shiro 的SecurityManager 實現及全部AuthorizingRealm實現都實現了CacheManagerAware測試

Shiro 提供了一個個當即可用的EhCacheManager 實現this

Caching 配置

經過在SecurityManager上設置了CacheManger,它反過來也會將它設置到實現了CacheManagerAware 的各類不一樣的Realm 上,示例以下:

    spring-shiro.xml配置

    <!-- 用戶受權信息Cache, 採用EhCache -->
    <bean id="shiroEhcacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager">
        <property name="cacheManagerConfigFile" value="classpath:spring/ehcache-shiro.xml" />
    </bean>

    ehcache-shiro.xml

    <ehcache updateCheck="false" name="shiroCache">

    <diskStore path="java.io.tmpdir" />
    <defaultCache maxElementsInMemory="10000" eternal="false"
        timeToIdleSeconds="120" timeToLiveSeconds="120" overflowToDisk="false"
        diskPersistent="true" diskExpiryThreadIntervalSeconds="120" />

    <cache name="shiro-activeSessionCache" maxElementsInMemory="10000"
        eternal="true" timeToLiveSeconds="0" timeToIdleSeconds="0"
        diskPersistent="true" overflowToDisk="false"
        diskExpiryThreadIntervalSeconds="600">
    </cache>    
</ehcache>

       name:緩存名稱。
       maxElementsInMemory:緩存最大個數。
       eternal:對象是否永久有效,一但設置了,timeout將不起做用。
       timeToIdleSeconds:設置對象在失效前的容許閒置時間(單位:秒)。僅當eternal=false對象不是永久有效時使用,可選屬性,默認值是0,也就是可閒置時間無窮大。
       timeToLiveSeconds:設置對象在失效前容許存活時間(單位:秒)。最大時間介於建立時間和失效時間之間。僅當eternal=false對象不是永久有效時使用,默認是0.,也就是對象存活時間無窮大。
       overflowToDisk:當內存中對象數量達到maxElementsInMemory時,Ehcache將會對象寫到磁盤中。
       diskSpoolBufferSizeMB:這個參數設置DiskStore(磁盤緩存)的緩存區大小。默認是30MB。每一個Cache都應該有本身的一個緩衝區。
       maxElementsOnDisk:硬盤最大緩存個數。
       diskPersistent:是否緩存虛擬機重啓期數據 Whether the disk store persists between restarts of the Virtual Machine. The default value is false.
       diskExpiryThreadIntervalSeconds:磁盤失效線程運行時間間隔,默認是120秒。
       memoryStoreEvictionPolicy:當達到maxElementsInMemory限制時,Ehcache將會根據指定的策略去清理內存。默認策略是LRU(最近最少使用)。你能夠設置爲FIFO(先進先出)或是LFU(較少使用)。
       clearOnFlush:內存數量最大時是否清除。

包裝使用其餘的Cache框架

能夠經過寫一個類來實現Shiro的CacheManager,在這個類裏面包裝使用任何你想要使用的Cache框架,這裏以使用Srping的緩存框架爲例,參考以下:

public class MyCacheManager implements CacheManager {

    public <K, V> Cache<K, V> getCache(String name) throws CacheException {

        org.springframework.cache.Cache springCache = cacheManager.getCache(name);

        return new SpringCacheWrapper(springCache);

    }

    class SpringCacheWrapper implements Cache {

        private org.springframework.cache.Cache springCache;

        SpringCacheWrapper(org.springframework.cache.Cache springCache) {

            this.springCache = springCache;

        }

        public Object get(Object key) throws CacheException {

            Object value = springCache.get(key);

            if (value instanceof SimpleValueWrapper) {

                return ((SimpleValueWrapper) value).get();

            }

            return value;

        }

    }

}

緩存數據同步更新的解決方案

使用Shiro的時候,緩存數據最大的問題就在於數據同步更新。

由於Shiro只負責驗證部分,若是應用程序修改了人員的權限,那麼就須要同步更新到Shiro裏面去,也就是要同步Shiro的緩存數據。

一個解決方案就是徹底廢棄Shiro的緩存機制,本身在應用中控制數據的緩存

這裏給出另外一種簡易可行的方案:

1:若是你使用的Spring,並且是自定義的Realm,那麼能夠在你的Realm裏面添加一個方法來刪除該用戶的緩存數據,這樣下次shiro在驗證這個用戶的時候,就會從新去獲取數據,從而實現數據的同步

2:因爲是自定義的Realm,能夠把該對象做爲Spring的bean,注入到你的業務對象中,在須要的時候就能夠調用該方法來刪除shiro的緩存數據了

示例,好比在前面自定義的MyRealm中,添加以下方法,示例以下:

public void removeUserCache(String userId){

  SimplePrincipalCollection pc = new SimplePrincipalCollection();

  pc.add(userId, super.getName()); 

  super.clearCachedAuthorizationInfo(pc);

}

而後在HelloAnno中進行測試,示例以下:

1:要注入MyRealm,但注意須要使用getter/setter來注入

2:在main方法中,示例以下:

public static void main(String[] args) {

   ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");

   HelloAnno t = (HelloAnno)ctx.getBean("helloAnno");

   t.login();

   t.t();

   t.t();

   t.getMr().removeUserCache("javass");

   t.t();

  }

相關文章
相關標籤/搜索