As we know, shiro manages sessions through Session manager, while Session operations are implemented through SessionDao. By default, shiro implements two kinds of SessionDao: cacheingsessiondao and MemorySessionDAO. When we use EhCache caching, we use cacheingsessiondao. When we do not use caching, we choose internal based caching Therefore, if we want to realize Redis based distributed Session sharing, the key is to rewrite the Session Dao in the Session manager. Our rewrite code is as follows:
package com.chhliu.springboot.shiro.cache; import java.io.Serializable; import java.util.Collection; import java.util.concurrent.TimeUnit; import org.apache.shiro.session.Session; import org.apache.shiro.session.UnknownSessionException; import org.apache.shiro.session.mgt.eis.AbstractSessionDAO; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Service; @Service @SuppressWarnings({ "rawtypes", "unchecked" }) public class RedisSessionDao extends AbstractSessionDAO { // Session timeout in milliseconds private long expireTime = 120000; @Autowired private RedisTemplate redisTemplate;// Redis operation class. For those unfamiliar with this, please refer to the previous blog public RedisSessionDao() { super(); } public RedisSessionDao(long expireTime, RedisTemplate redisTemplate) { super(); this.expireTime = expireTime; this.redisTemplate = redisTemplate; } @Override // Update session public void update(Session session) throws UnknownSessionException { System.out.println("===============update================"); if (session == null || session.getId() == null) { return; } session.setTimeout(expireTime); redisTemplate.opsForValue().set(session.getId(), session, expireTime, TimeUnit.MILLISECONDS); } @Override // Delete session public void delete(Session session) { System.out.println("===============delete================"); if (null == session) { return; } redisTemplate.opsForValue().getOperations().delete(session.getId()); } @Override// Get the active session, which can be used to count the number of people online. If you want to achieve this function, you can specify a session prefix when adding the session to redis, and use the keys ("session prefix *") method to fuzzy find all session combinations in redis public Collection<Session> getActiveSessions() { System.out.println("==============getActiveSessions================="); return redisTemplate.keys("*"); } @Override// Join session protected Serializable doCreate(Session session) { System.out.println("===============doCreate================"); Serializable sessionId = this.generateSessionId(session); this.assignSessionId(session, sessionId); redisTemplate.opsForValue().set(session.getId(), session, expireTime, TimeUnit.MILLISECONDS); return sessionId; } @Override// Read session protected Session doReadSession(Serializable sessionId) { System.out.println("==============doReadSession================="); if (sessionId == null) { return null; } return (Session) redisTemplate.opsForValue().get(sessionId); } public long getExpireTime() { return expireTime; } public void setExpireTime(long expireTime) { this.expireTime = expireTime; } public RedisTemplate getRedisTemplate() { return redisTemplate; } public void setRedisTemplate(RedisTemplate redisTemplate) { this.redisTemplate = redisTemplate; } }
After the implementation of SessionDao, we need to add SessionDao to the SessionManager. The code is as follows:
@Bean public DefaultWebSessionManager configWebSessionManager(){ DefaultWebSessionManager manager = new DefaultWebSessionManager(); manager.setCacheManager(cacheManager);// Join cache manager manager.setSessionDAO(sessionDao);// Set SessionDao manager.setDeleteInvalidSessions(true);// Delete expired session s manager.setGlobalSessionTimeout(sessionDao.getExpireTime());// Set global session timeout manager.setSessionValidationSchedulerEnabled(true);// Check session regularly return manager; }
The last step is to configure the session manager into the security manager
@Bean public SecurityManager securityManager(DefaultWebSessionManager webSessionManager) { DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); // Set realm. securityManager.setRealm(myShiroRealm()); // Injection cache manager; securityManager.setCacheManager(cacheManager);// If this is executed multiple times, it is the same object; // session manager securityManager.setSessionManager(webSessionManager); //Inject remember me manager; securityManager.setRememberMeManager(rememberMeManager()); return securityManager; }
The test results are as follows:
==============doReadSession================= ==============doReadSession================= ==============doReadSession================= ==============doReadSession================= ==============doReadSession================= ==============doReadSession================= ==============doReadSession================= ==============doReadSession================= ==============doReadSession================= ==============doReadSession================= ==============doReadSession================= ==============doReadSession================= ===============update================ ==============doReadSession================= ==============doReadSession================= ===============update================ ==============doReadSession================= ==============doReadSession================= ==============doReadSession================= Permission configuration -- > myshirorealm. Dogetauthorizationinfo() ==============doReadSession=================
We will find that when there are multiple resources in a page, we will constantly call the doReadSession and update methods to read and update the session. At present, we haven't come up with a better solution to this problem.