SpringBoot中Shiro快取使用Redis、Ehcache

雲揚四海發表於2019-09-06

在SpringBoot中Shiro快取使用Redis、Ehcache實現的兩種方式例項

SpringBoot 中配置redis作為session 快取器。 讓shiro引用

  • 本文是建立在你是使用這shiro基礎之上的補充內容

第一種:Redis快取,將資料儲存到redis 並且開啟session存入redis中。

引入pom

    <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.session</groupId>
            <artifactId>spring-session-data-redis</artifactId>
     </dependency>
複製程式碼

配置redisConfig

@Configuration
@EnableCaching
public class RedisConfig extends CachingConfigurerSupport {
    @Bean
    public KeyGenerator keyGenerator() {
        return new KeyGenerator() {
            @Override
            public Object generate(Object target, Method method, Object... params) {
                StringBuilder sb = new StringBuilder();
                sb.append(target.getClass().getName());
                sb.append(method.getName());
                for (Object obj : params) {
                    sb.append(obj.toString());
                }
                return sb.toString();
            }
        };
    }
    

    @Bean
    //在這裡配置快取reids配置
    public RedisCacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
        RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig()
                .entryTtl(Duration.ofHours(1)); // 設定快取有效期一小時
        System.out.println("《========【開啟redis】 ======== 》 ");
        return RedisCacheManager
                .builder(RedisCacheWriter.nonLockingRedisCacheWriter(redisConnectionFactory))
                .cacheDefaults(redisCacheConfiguration).build();
    }

    @Bean
    public RedisTemplate<String, String> redisTemplate(RedisConnectionFactory factory) {
        StringRedisTemplate template = new StringRedisTemplate(factory);
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(om);
        template.setValueSerializer(jackson2JsonRedisSerializer);
        template.afterPropertiesSet();
        return template;
    }

}

複製程式碼

配置自定義快取管理器,引入redis快取管理器

  • 定義自己的CacheManager
/**
 * <p> 自定義cacheManage 擴張shiro裡面的快取 使用reids作快取 </p> 
 * <description>
 *  引入自己定義的CacheManager 
 *  關於CacheManager的配置檔案在spring-redis-cache.xml中
 * </description>
 */
@Component
public class ShiroSpringCacheManager implements CacheManager ,Destroyable{
    /**
     * 將之上的RedisCacheManager的Bean拿出來 注入於此
     */
    @Autowired
    private org.springframework.cache.CacheManager cacheManager;
     
    public org.springframework.cache.CacheManager getCacheManager() {
        return cacheManager;
    }

    public void setCacheManager(org.springframework.cache.CacheManager cacheManager) {
        this.cacheManager = cacheManager;
    }

    @Override
    public void destroy() throws Exception {
        cacheManager = null;
    }

    @Override
    public <K, V> Cache<K, V> getCache(String name)  {
        if (name == null ){
            return null;
        }
        // 新建一個ShiroSpringCache 將Bean放入並例項化
        return new ShiroSpringCache<K,V>(name,getCacheManager());
    }


}
複製程式碼
  • 定義自己實現的Shiro的Cache,實現了Shiro包裡的Cache
/**
 * <p> 自定義快取 將資料存入到redis中 </p>
 */
@SuppressWarnings("unchecked")
public class ShiroSpringCache<K,V> implements org.apache.shiro.cache.Cache<K, V>{
    private static final Logger log = LoggerFactory.getLogger(ShiroSpringCache.class);
    private CacheManager cacheManager;
    private Cache cache;

    public ShiroSpringCache(String name, CacheManager cacheManager) {
        if(name==null || cacheManager==null){
            throw new IllegalArgumentException("cacheManager or CacheName cannot be null.");
        }
        this.cacheManager = cacheManager;
        //這裡首先是從父類中獲取這個cache,如果沒有會建立一個redisCache,初始化這個redisCache的時候
        //會設定它的過期時間如果沒有配置過這個快取的,那麼預設的快取時間是為0的,如果配置了,就會把配置的時間賦予給這個RedisCache
        //如果從快取的過期時間為0,就表示這個RedisCache不存在了,這個redisCache實現了spring中的cache
        this.cache= cacheManager.getCache(name);
    }
    @Override
    public V get(K key) throws CacheException {
        log.info("從快取中獲取key為{}的快取資訊",key);
        if(key == null){
            return null;
        }
        ValueWrapper valueWrapper = cache.get(key);
        if(valueWrapper==null){
            return null;
        }
        return (V) valueWrapper.get();
    }

    @Override
    public V put(K key, V value) throws CacheException {
        log.info("建立新的快取,資訊為:{}={}",key,value);
        cache.put(key, value);
        return get(key);
    }

    @Override
    public V remove(K key) throws CacheException {
        log.info("幹掉key為{}的快取",key);
        V v = get(key);
        cache.evict(key);//幹掉這個名字為key的快取
        return v;
    }

    @Override
    public void clear() throws CacheException {
        log.info("清空所有的快取");
        cache.clear();
    }

    @Override
    public int size() {
        return cacheManager.getCacheNames().size();
    }

    /**
     * 獲取快取中所的key值
     */
    @Override
    public Set<K> keys() {
        return (Set<K>) cacheManager.getCacheNames();
    }

    /**
     * 獲取快取中所有的values值
     */
    @Override
    public Collection<V> values() {
        return (Collection<V>) cache.get(cacheManager.getCacheNames()).get();
    }

    @Override
    public String toString() {
        return "ShiroSpringCache [cache=" + cache + "]";
    }
}
複製程式碼
  • 到此為止,使用redis做快取,和spring的整合就完成了。

  • 可以使用以下註解將快取放入redis

 @Cacheable(value = Cache.CONSTANT, key = "'" + CacheKey.DICT_NAME + "'+#name+'_'+#val")
複製程式碼

配置spring session管理器

    @Bean
    @ConditionalOnProperty(prefix = "xpro", name = "spring-session-open", havingValue = "true")
    public ServletContainerSessionManager servletContainerSessionManager() {
        return new ServletContainerSessionManager();
    }
複製程式碼
  • 新建類 spring session設定session過期時間
/**
 * spring session配置
 *
 * @author xingri
 * @date 2017-07-13 21:05
 */
@EnableRedisHttpSession(maxInactiveIntervalInSeconds = 900)  //session過期時間  如果部署多機環境,需要開啟註釋
@ConditionalOnProperty(prefix = "xpro", name = "spring-session-open", havingValue = "true")
public class SpringSessionConfig {

}

複製程式碼

第一種:Ehcache做快取,可以將資料儲存到磁碟中,也可以存到記憶體中

  • 新建ehcache.xml 檔案
<?xml version="1.0" encoding="UTF-8"?>
<ehcache updateCheck="false" dynamicConfig="false">
    <diskStore path="java.io.tmpdir"/>
    <!--授權資訊快取-->
    <cache name="authorizationCache"
           maxEntriesLocalHeap="2000"
           eternal="false"
           timeToIdleSeconds="1800"
           timeToLiveSeconds="1800"
           overflowToDisk="false"
           statistics="true">
    </cache>
<!--身份資訊快取-->
    <cache name="authenticationCache"
           maxEntriesLocalHeap="2000"
           eternal="false"
           timeToIdleSeconds="1800"
           timeToLiveSeconds="1800"
           overflowToDisk="false"
           statistics="true">
    </cache>
<!--session快取-->
    <cache name="activeSessionCache"
           maxEntriesLocalHeap="2000"
           eternal="false"
           timeToIdleSeconds="1800"
           timeToLiveSeconds="1800"
           overflowToDisk="false"
           statistics="true">
    </cache>

    <!-- 快取半小時 -->
    <cache name="halfHour"
           maxElementsInMemory="10000"
           maxElementsOnDisk="100000"
           eternal="false"
           timeToIdleSeconds="1800"
           timeToLiveSeconds="1800"
           overflowToDisk="false"
           diskPersistent="false" />

    <!-- 快取一小時 -->
    <cache name="hour"
           maxElementsInMemory="10000"
           maxElementsOnDisk="100000"
           eternal="false"
           timeToIdleSeconds="3600"
           timeToLiveSeconds="3600"
           overflowToDisk="false"
           diskPersistent="false" />

    <!-- 快取一天 -->
    <cache name="oneDay"
           maxElementsInMemory="10000"
           maxElementsOnDisk="100000"
           eternal="false"
           timeToIdleSeconds="86400"
           timeToLiveSeconds="86400"
           overflowToDisk="false"
           diskPersistent="false" />

    <!--
        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:記憶體數量最大時是否清除。
    -->
    <defaultCache name="defaultCache"
                  maxElementsInMemory="10000"
                  eternal="false"
                  timeToIdleSeconds="600"
                  timeToLiveSeconds="600"
                  overflowToDisk="false"
                  maxElementsOnDisk="100000"
                  diskPersistent="false"
                  diskExpiryThreadIntervalSeconds="120"
                  memoryStoreEvictionPolicy="LRU"/>

</ehcache>
複製程式碼

配置自定義快取管理器,引入ehcache快取管理器


/**
 * ehcache配置
 *
 */
@Configuration
@EnableCaching
public class EhCacheConfig {

    /**
     * EhCache的配置
     */
    @Bean
    public EhCacheCacheManager cacheManager(CacheManager cacheManager) {
        MBeanServer mBeanServer = ManagementFactory.getPlatformMBeanServer();
        ManagementService.registerMBeans(cacheManager, mBeanServer, true, true, true, true);
        return new EhCacheCacheManager(cacheManager);
    }

    /**
     * EhCache的配置
     */
    @Bean
    public EhCacheManagerFactoryBean ehcache() {
        System.out.println("《========【開啟ehcache】 ======== 》 ");
        EhCacheManagerFactoryBean ehCacheManagerFactoryBean = new EhCacheManagerFactoryBean();
        ehCacheManagerFactoryBean.setConfigLocation(new ClassPathResource("ehcache.xml"));
        return ehCacheManagerFactoryBean;
    }
    
    @Bean
    public org.apache.shiro.cache.CacheManager getCacheShiroManager(EhCacheManagerFactoryBean ehcache) {
        EhCacheManager ehCacheManager = new EhCacheManager();
        ehCacheManager.setCacheManager(ehcache.getObject());
        return ehCacheManager;
    }
}

複製程式碼

最後 最重要的是引入shriro 中


/**
 * shiro許可權管理的配置
 *
 */
@Configuration
public class ShiroConfig {

    /**
     * 安全管理器
     */
    @Bean
    public DefaultWebSecurityManager securityManager(CookieRememberMeManager rememberMeManager, CacheManager cacheShiroManager, SessionManager sessionManager) {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setAuthenticator(modularRealmAuthenticator());

        List<Realm> realms=new ArrayList<>();
        securityManager.setRealms(realms);

        securityManager.setCacheManager(cacheShiroManager);

        securityManager.setRememberMeManager(rememberMeManager);
        securityManager.setSessionManager(sessionManager);
        return securityManager;
    }


    /**
     * spring session管理器(多機環境)
     */
    @Bean
    public ServletContainerSessionManager servletContainerSessionManager() {
        return new ServletContainerSessionManager();
    }

    /**
     * session管理器(單機環境) 使用cookie儲存快取。。如果多級請註釋
     */
    @Bean
    public DefaultWebSessionManager defaultWebSessionManager(CacheManager cacheShiroManager, XProProperties xProProperties) {
        DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
        sessionManager.setCacheManager(cacheShiroManager);
        sessionManager.setSessionValidationInterval(xProProperties.getSessionValidationInterval() * 1000);
        sessionManager.setGlobalSessionTimeout(xProProperties.getSessionInvalidateTime() * 1000);
        sessionManager.setDeleteInvalidSessions(true);
        sessionManager.setSessionValidationSchedulerEnabled(true);
        Cookie cookie = new SimpleCookie(ShiroHttpSession.DEFAULT_SESSION_ID_NAME);
        cookie.setName("shiroCookie");
        cookie.setHttpOnly(true);
        sessionManager.setSessionIdCookie(cookie);
        return sessionManager;
    }



    /**
     * 快取管理器 使用Ehcache實現 如果使用redis則註釋下面內容!!!! 
     */
    @Bean
    public CacheManager getCacheShiroManager(EhCacheManagerFactoryBean ehcache) {
        EhCacheManager ehCacheManager = new EhCacheManager();
        ehCacheManager.setCacheManager(ehcache.getObject());
        return ehCacheManager;
    }



    /**
     * 專案自定義的Realm
     */
    @Bean
    public ShiroDbRealm shiroDbRealm() {
        return new ShiroDbRealm();
    }

    @Bean
    public ShiroTockenRealm shiroTockenRealm( ) {
        return new ShiroTockenRealm();
    }

    @Bean
    public ShiroJwtRealm shiroJwtRealm( ) {
        return new ShiroJwtRealm();
    }

    /**
     * 系統自帶的Realm管理,主要針對多realm
     * */
    @Bean
    public ModularRealmAuthenticator modularRealmAuthenticator(){
        ModularRealmAuthenticator modularRealmAuthenticator=new ModularRealmAuthenticator();
        modularRealmAuthenticator.setAuthenticationStrategy(new AtLeastOneSuccessfulStrategy());
        return modularRealmAuthenticator;
    }
    /**
     * rememberMe管理器, cipherKey生成見{@code Base64Test.java}
     */
    @Bean
    public CookieRememberMeManager rememberMeManager(SimpleCookie rememberMeCookie) {
        CookieRememberMeManager manager = new CookieRememberMeManager();
        manager.setCipherKey(Base64.decode("Z3VucwAAAAAAAAAAAAAAAA=="));
        manager.setCookie(rememberMeCookie);
        return manager;
    }

    /**
     * 記住密碼Cookie
     */
    @Bean
    public SimpleCookie rememberMeCookie() {
        SimpleCookie simpleCookie = new SimpleCookie("rememberMe");
        simpleCookie.setHttpOnly(true);
        simpleCookie.setMaxAge(7 * 24 * 60 * 60);//7天
        return simpleCookie;
    }


    /**
     * 在方法中 注入 securityManager,進行代理控制
     */
    @Bean
    public MethodInvokingFactoryBean methodInvokingFactoryBean(DefaultWebSecurityManager securityManager) {
        MethodInvokingFactoryBean bean = new MethodInvokingFactoryBean();
        bean.setStaticMethod("org.apache.shiro.SecurityUtils.setSecurityManager");
        bean.setArguments(new Object[]{securityManager});
        return bean;
    }

    /**
     * 保證實現了Shiro內部lifecycle函式的bean執行
     */
    @Bean
    public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
        return new LifecycleBeanPostProcessor();
    }

    /**
     * 啟用shrio授權註解攔截方式,AOP式方法級許可權檢查
     */
    @Bean
    @DependsOn(value = "lifecycleBeanPostProcessor") //依賴其他bean的初始化
    public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
        return new DefaultAdvisorAutoProxyCreator();
    }

    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(DefaultWebSecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor =
                new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
        return authorizationAttributeSourceAdvisor;
    }

}

複製程式碼

相關文章