Shiro cache in SpringBook uses Redis and Ehcache

Two examples of Shiro cache implementation using Redis and Ehcache in SpringBook

redis is configured as session buffer in SpringBook. Let shiro quote

  • This article is a supplement based on your use of shiro.

The first is Redis cache, which stores data in redis and opens session s in redis.

Introducing 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>

Configure 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
    //Configure the cache reids configuration here
    public RedisCacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
        RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig()
                .entryTtl(Duration.ofHours(1)); // Set cache validity for one hour
        System.out.println("<========[open 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;
    }

}

Configure custom cache manager and introduce redis cache manager

  • Define your own Cache Manager
/**
 * <p> Custom cache management expands the cache in shiro using reids as the cache</p> 
 * <description>
 *  Introduce a Cache Manager of your own definition 
 *  The configuration file for CacheManager is in spring-redis-cache.xml
 * </description>
 */
@Component
public class ShiroSpringCacheManager implements CacheManager ,Destroyable{
    /**
     * Put the RedisCache Manager Bean above it and inject it here
     */
    @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;
        }
        // Create a new ShiroSpring Cache to put beans in and instantiate them
        return new ShiroSpringCache<K,V>(name,getCacheManager());
    }


}
  • Define your own implementation of Shiro's Cache, and implement the Cache in Shiro's package
/**
 * <p> Custom caching stores data in 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;
        //The first step here is to get the cache from the parent class. If not, a redisCache will be created to initialize the redisCache.
        //If the cache is not configured, the default cache time is 0. If it is configured, the configuration time is given to the RedisCache.
        //If the expiration time from the cache is 0, the RedisCache does not exist, and the redisCache implements the cache in spring.
        this.cache= cacheManager.getCache(name);
    }
    @Override
    public V get(K key) throws CacheException {
        log.info("Get from the cache key by{}Cache information",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 following information:{}={}",key,value);
        cache.put(key, value);
        return get(key);
    }

    @Override
    public V remove(K key) throws CacheException {
        log.info("Kill key by{}Caching",key);
        V v = get(key);
        cache.evict(key);//Get rid of the cache with the name key
        return v;
    }

    @Override
    public void clear() throws CacheException {
        log.info("Clear all caches");
        cache.clear();
    }

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

    /**
     * Get the key value in the cache
     */
    @Override
    public Set<K> keys() {
        return (Set<K>) cacheManager.getCacheNames();
    }

    /**
     * Get all values in the cache
     */
    @Override
    public Collection<V> values() {
        return (Collection<V>) cache.get(cacheManager.getCacheNames()).get();
    }

    @Override
    public String toString() {
        return "ShiroSpringCache [cache=" + cache + "]";
    }
}
  • So far, the integration of spring and redis for caching has been completed.

  • You can use the following annotations to put caches into redis

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

Configure spring session manager

    @Bean
    @ConditionalOnProperty(prefix = "xpro", name = "spring-session-open", havingValue = "true")
    public ServletContainerSessionManager servletContainerSessionManager() {
        return new ServletContainerSessionManager();
    }
  • New class spring session sets session expiration time
/**
 * spring session To configure
 *
 * @author xingri
 * @date 2017-07-13 21:05
 */
@EnableRedisHttpSession(maxInactiveIntervalInSeconds = 900)  //session expiration time If you deploy a multi-machine environment, you need to open comments
@ConditionalOnProperty(prefix = "xpro", name = "spring-session-open", havingValue = "true")
public class SpringSessionConfig {

}

First: Ehcache caches, which store data either on disk or in memory.

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

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

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

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

    <!--
        name:Cache name.
        maxElementsInMemory: Maximum number of caches.
        eternal:Whether the object is permanently valid, once set, timeout It will not work.
        timeToIdleSeconds: Set the allowable idle time (in seconds) of the object before it fails. Only when eternal=false Objects are not used when they are permanently valid, optional attributes, and the default value is 0, which means that the idle time is infinite.
        timeToLiveSeconds: Sets the allowable lifetime (in seconds) of the object before it fails. The maximum time is between the creation time and the failure time. Only when eternal=false Objects that are not permanently valid are used by default 0.,That is, the object has an infinite lifetime.
        overflowToDisk: When the number of objects in memory reaches maxElementsInMemory At that time, Ehcache Write objects to disk.
        diskSpoolBufferSizeMB: This parameter setting DiskStore(Cache size of disk cache. The default is 30. MB. each Cache They should all have their own buffer.
        maxElementsOnDisk: Maximum number of hard disk caches.
        diskPersistent: Whether to cache virtual machine restart date data Whether the disk store persists between restarts of the Virtual Machine. The default value is false.
        diskExpiryThreadIntervalSeconds: Disk failure threads run at a time interval of 120 seconds by default.
        memoryStoreEvictionPolicy: When reach maxElementsInMemory When limiting, Ehcache Memory will be cleaned up according to the specified policy. The default policy is LRU(Minimum use recently. You can set it to FIFO(First in first out) or LFU(Less use.
        clearOnFlush: Whether to clear the maximum amount of memory.
    -->
    <defaultCache name="defaultCache"
                  maxElementsInMemory="10000"
                  eternal="false"
                  timeToIdleSeconds="600"
                  timeToLiveSeconds="600"
                  overflowToDisk="false"
                  maxElementsOnDisk="100000"
                  diskPersistent="false"
                  diskExpiryThreadIntervalSeconds="120"
                  memoryStoreEvictionPolicy="LRU"/>

</ehcache>

Configure custom cache manager and introduce ehcache cache manager

/**
 * ehcache To configure
 *
 */
@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() {
        System.out.println("<========[open 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;
    }
}

Finally, the most important thing is to introduce shriro

/**
 * shiro Configuration of Privilege Management
 *
 */
@Configuration
public class ShiroConfig {

    /**
     * Safety 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() {
        return new ServletContainerSessionManager();
    }

    /**
     * session The manager (stand-alone environment) uses cookie s to store caches. If multilevel, 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;
    }



    /**
     * Cache Manager uses Ehcache to implement if redis is used then annotate the following content!!!! 
     */
    @Bean
    public CacheManager getCacheShiroManager(EhCacheManagerFactoryBean ehcache) {
        EhCacheManager ehCacheManager = new EhCacheManager();
        ehCacheManager.setCacheManager(ehcache.getObject());
        return ehCacheManager;
    }



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

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

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

    /**
     * Realm management of the system, mainly for multi-realm
     * */
    @Bean
    public ModularRealmAuthenticator modularRealmAuthenticator(){
        ModularRealmAuthenticator modularRealmAuthenticator=new ModularRealmAuthenticator();
        modularRealmAuthenticator.setAuthenticationStrategy(new AtLeastOneSuccessfulStrategy());
        return modularRealmAuthenticator;
    }
    /**
     * rememberMe Manager, cipherKey generation see {@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() {
        SimpleCookie simpleCookie = new SimpleCookie("rememberMe");
        simpleCookie.setHttpOnly(true);
        simpleCookie.setMaxAge(7 * 24 * 60 * 60);//7 days
        return simpleCookie;
    }


    /**
     * Introducing Security Manager 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;
    }

    /**
     * Ensuring the execution of bean s that implement lifecycle functions within Shiro
     */
    @Bean
    public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
        return new LifecycleBeanPostProcessor();
    }

    /**
     * Enable shrio authorization annotation interception, AOP method level permission check
     */
    @Bean
    @DependsOn(value = "lifecycleBeanPostProcessor") //Initialization of dependencies on other bean s
    public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
        return new DefaultAdvisorAutoProxyCreator();
    }

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

}

Keywords: Java Ehcache Redis Session Shiro

Added by ryanwood4 on Mon, 07 Oct 2019 20:53:50 +0300