Shiro caching in SpringBoot uses two instances of Redis and Ehcache implementations

Redis is configured in SpringBoot as the session cache. Let the shiro reference

  • This article is a supplement based on the fact that you use Shiro

The first is Redis cache, which stores data to Redis and opens a session to store it in Redis.

The introduction of 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>
Copy the code

Configuration redisConfig

@Configuration
@EnableCaching
public class RedisConfig extends CachingConfigurerSupport {
    @Bean
    public KeyGenerator keyGenerator(a) {
        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());
                }
                returnsb.toString(); }}; }@Bean
    // Configure the cache REIDS configuration here
    public RedisCacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
        RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig()
                .entryTtl(Duration.ofHours(1)); // Set the cache validity period to one hour
        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();
        returntemplate; }}Copy the code

Configure the custom cache manager and introduce the Redis cache manager

  • Define your own CacheManager
/** * 

Customize cacheManage to expand the cache in Shiro using REIDS

* * The CacheManager configuration file is in spring-redis-cache.xml * */
@Component public class ShiroSpringCacheManager implements CacheManager ,Destroyable{ /** * take the RedisCacheManager Bean and inject it */ @Autowired private org.springframework.cache.CacheManager cacheManager; public org.springframework.cache.CacheManager getCacheManager(a) { return cacheManager; } public void setCacheManager(org.springframework.cache.CacheManager cacheManager) { this.cacheManager = cacheManager; } @Override public void destroy(a) throws Exception { cacheManager = null; } @Override public <K, V> Cache<K, V> getCache(String name) { if (name == null) {return null; } // Create a new ShiroSpringCache to put in and instantiate the Bean return newShiroSpringCache<K,V>(name,getCacheManager()); }}Copy the code
  • Define your own implementation of Shiro’s Cache, which implements the Cache in Shiro packages
/** * 

Custom caches store data in redis

*/
@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; // redisCache is initialized from the parent class. If not, redisCache will be created If the cache is not configured, the default cache time is 0. If the cache is configured, the configured time is assigned to the RedisCache // If the expiration time of the slave cache is 0, the RedisCache does not exist. The RedisCache implements the spring cache this.cache= cacheManager.getCache(name); } @Override public V get(K key) throws CacheException { log.info("Retrieve cache information from cache with 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("Create a new cache with the message: {}={}",key,value); cache.put(key, value); return get(key); } @Override public V remove(K key) throws CacheException { log.info("Kill cache with key {}",key); V v = get(key); cache.evict(key);// Kill the cache named key return v; } @Override public void clear(a) throws CacheException { log.info("Clear all caches."); cache.clear(); } @Override public int size(a) { return cacheManager.getCacheNames().size(); } /** * get the cache key */ @Override public Set<K> keys(a) { return (Set<K>) cacheManager.getCacheNames(); } /** * get all values in the cache */ @Override public Collection<V> values(a) { return (Collection<V>) cache.get(cacheManager.getCacheNames()).get(); } @Override public String toString(a) { return "ShiroSpringCache [cache=" + cache + "]"; }}Copy the code
  • At this point, using Redis for caching, the integration with Spring is complete.

  • You can put the cache into Redis using the following annotations

 @Cacheable(value = Cache.CONSTANT, key = "'" + CacheKey.DICT_NAME + "'+#name+'_'+#val")
Copy the code

Configure the Spring Session Manager

    @Bean
    @ConditionalOnProperty(prefix = "xpro", name = "spring-session-open", havingValue = "true")
    public ServletContainerSessionManager servletContainerSessionManager() {
        return new ServletContainerSessionManager();
    }
Copy the code
  • Create a class spring Session to set the session expiration time
/** * Spring Session configuration ** @author xingri * @date 2017-07-13 21:05 */ @ EnableRedisHttpSession (maxInactiveIntervalInSeconds = 900) / / session expiration time If the deployment environment, more need to open the comments @ ConditionalOnProperty (prefix ="xpro", name = "spring-session-open", havingValue = "true")
public class SpringSessionConfig {

}

Copy the code

Ehcache is used to cache data, which can be stored in disk or memory

  • Create the ehcache. XML file
<?xml version="1.0" encoding="UTF-8"? >
<ehcache updateCheck="false" dynamicConfig="false">
    <diskStore path="java.io.tmpdir"/>
    <! -- Authorization information cache -->
    <cache name="authorizationCache"
           maxEntriesLocalHeap="2000"
           eternal="false"
           timeToIdleSeconds="1800"
           timeToLiveSeconds="1800"
           overflowToDisk="false"
           statistics="true">
    </cache>
<! -- Identity cache -->
    <cache name="authenticationCache"
           maxEntriesLocalHeap="2000"
           eternal="false"
           timeToIdleSeconds="1800"
           timeToLiveSeconds="1800"
           overflowToDisk="false"
           statistics="true">
    </cache>
<! - the session cache - >
    <cache name="activeSessionCache"
           maxEntriesLocalHeap="2000"
           eternal="false"
           timeToIdleSeconds="1800"
           timeToLiveSeconds="1800"
           overflowToDisk="false"
           statistics="true">
    </cache>

    <! -- Cache half an hour -->
    <cache name="halfHour"
           maxElementsInMemory="10000"
           maxElementsOnDisk="100000"
           eternal="false"
           timeToIdleSeconds="1800"
           timeToLiveSeconds="1800"
           overflowToDisk="false"
           diskPersistent="false" />

    <! -- Cache for one hour -->
    <cache name="hour"
           maxElementsInMemory="10000"
           maxElementsOnDisk="100000"
           eternal="false"
           timeToIdleSeconds="3600"
           timeToLiveSeconds="3600"
           overflowToDisk="false"
           diskPersistent="false" />

    <! -- Cache for a day -->
    <cache name="oneDay"
           maxElementsInMemory="10000"
           maxElementsOnDisk="100000"
           eternal="false"
           timeToIdleSeconds="86400"
           timeToLiveSeconds="86400"
           overflowToDisk="false"
           diskPersistent="false" />

    <! -- name: indicates the cache name. MaxElementsInMemory: Maximum number of caches. Eternal: Whether the object is permanent. Once it is set, timeout will not work. TimeToIdleSeconds: Sets the time (in seconds) that the object is allowed to remain idle before becoming invalid. Used only when eternal=false object is not permanently valid, optional property, default is 0, that is infinite idle time. TimeToLiveSeconds: Sets the time (in seconds) that the object is allowed to live before becoming invalid. The maximum time is between the creation time and the expiration time. Used only when eternal=false object is not permanently valid, the default is 0., that is, the object lifetime is infinite. OverflowToDisk: Ehcache writes objects to disk when the number of objects in memory reaches maxElementsInMemory. DiskSpoolBufferSizeMB: This parameter sets the size of the DiskStore cache. The default is 30MB. Each Cache should have its own buffer. MaxElementsOnDisk: indicates the maximum number of caches on a hard disk. DiskPersistent: Whether the disk store inside between restarts of the Virtual Machine. The default value is false. DiskExpiryThreadIntervalSeconds: disk failure thread running time interval, the default is 120 seconds. MemoryStoreEvictionPolicy: when maxElementsInMemory limit is reached, Ehcache will be based on the specified strategies to clear the memory. The default policy is LRU (least recently used). You can set it to FIFO (First in, first out) or LFU (less used). ClearOnFlush: Specifies whether to flush the maximum memory. -->
    <defaultCache name="defaultCache"
                  maxElementsInMemory="10000"
                  eternal="false"
                  timeToIdleSeconds="600"
                  timeToLiveSeconds="600"
                  overflowToDisk="false"
                  maxElementsOnDisk="100000"
                  diskPersistent="false"
                  diskExpiryThreadIntervalSeconds="120"
                  memoryStoreEvictionPolicy="LRU"/>

</ehcache>
Copy the code

Configure a custom cache manager and introduce ehCache manager


/** * Ehcache configuration ** /
@Configuration
@EnableCaching
public class EhCacheConfig {

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

    /** * EhCache configuration */
    @Bean
    public EhCacheManagerFactoryBean ehcache(a) {
        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());
        returnehCacheManager; }}Copy the code

Last but not least, it is introduced into shriro


/** * Shiro permission management configuration ** /
@Configuration
public class ShiroConfig {

    /** * Security manager */
    @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 Manager (multi-machine environment) */
    @Bean
    public ServletContainerSessionManager servletContainerSessionManager(a) {
        return new ServletContainerSessionManager();
    }

    /** * The session manager (standalone environment) uses cookies to store the cache. If multiple levels please comment */
    @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;
    }



    /** * The cache manager is implemented using Ehcache. If redis is used, comment below !!!! * /
    @Bean
    public CacheManager getCacheShiroManager(EhCacheManagerFactoryBean ehcache) {
        EhCacheManager ehCacheManager = new EhCacheManager();
        ehCacheManager.setCacheManager(ehcache.getObject());
        return ehCacheManager;
    }



    /** * Project custom Realm */
    @Bean
    public ShiroDbRealm shiroDbRealm(a) {
        return new ShiroDbRealm();
    }

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

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

    /** * The system's own Realm management, mainly for multi-realm ** /
    @Bean
    public ModularRealmAuthenticator modularRealmAuthenticator(a){
        ModularRealmAuthenticator modularRealmAuthenticator=new ModularRealmAuthenticator();
        modularRealmAuthenticator.setAuthenticationStrategy(new AtLeastOneSuccessfulStrategy());
        return modularRealmAuthenticator;
    }
    /** * rememberMe manager@code Base64Test.java}
     */
    @Bean
    public CookieRememberMeManager rememberMeManager(SimpleCookie rememberMeCookie) {
        CookieRememberMeManager manager = new CookieRememberMeManager();
        manager.setCipherKey(Base64.decode("Z3VucwAAAAAAAAAAAAAAAA=="));
        manager.setCookie(rememberMeCookie);
        return manager;
    }

    /** * remember the password Cookie */
    @Bean
    public SimpleCookie rememberMeCookie(a) {
        SimpleCookie simpleCookie = new SimpleCookie("rememberMe");
        simpleCookie.setHttpOnly(true);
        simpleCookie.setMaxAge(7 * 24 * 60 * 60);/ / 7 days
        return simpleCookie;
    }


    /** * Inject the securityManager into the method for proxy control */
    @Bean
    public MethodInvokingFactoryBean methodInvokingFactoryBean(DefaultWebSecurityManager securityManager) {
        MethodInvokingFactoryBean bean = new MethodInvokingFactoryBean();
        bean.setStaticMethod("org.apache.shiro.SecurityUtils.setSecurityManager");
        bean.setArguments(new Object[]{securityManager});
        return bean;
    }

    /** * ensures that beans implementing Shiro's internal lifecycle function execute */
    @Bean
    public LifecycleBeanPostProcessor lifecycleBeanPostProcessor(a) {
        return new LifecycleBeanPostProcessor();
    }

    /** * Enable shrio authorization annotation interception mode, AOP-style method level permission checking */
    @Bean
    @DependsOn(value = "lifecycleBeanPostProcessor") // Depends on the initialization of other beans
    public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator(a) {
        return new DefaultAdvisorAutoProxyCreator();
    }

    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(DefaultWebSecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor =
                new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
        returnauthorizationAttributeSourceAdvisor; }}Copy the code