我们知道,shiro是通过SessionManager来管理Session的,而对于Session的操作则是通过SessionDao来实现的,默认的情况下,shiro实现了两种SessionDao,分别为CachingSessionDAO和MemorySessionDAO,当我们使用EhCache缓存时,则是使用的CachingSessionDAO,不适用缓存的情况下,就会选择基于内存的SessionDao.所以,如果我们想实现基于Redis的分布式Session共享,重点在于重写SessionManager中的SessionDao。我们的重写代码如下:
- 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超时时间,单位为毫秒
- private long expireTime = 120000;
-
- @Autowired
- private RedisTemplate redisTemplate;// Redis操作类,对这个使用不熟悉的,可以参考前面的博客
-
- public RedisSessionDao() {
- super();
- }
-
- public RedisSessionDao(long expireTime, RedisTemplate redisTemplate) {
- super();
- this.expireTime = expireTime;
- this.redisTemplate = redisTemplate;
- }
-
- @Override // 更新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 // 删除session
- public void delete(Session session) {
- System.out.println("===============delete================");
- if (null == session) {
- return;
- }
- redisTemplate.opsForValue().getOperations().delete(session.getId());
- }
-
- @Override// 获取活跃的session,可以用来统计在线人数,如果要实现这个功能,可以在将session加入redis时指定一个session前缀,统计的时候则使用keys("session-prefix*")的方式来模糊查找redis中所有的session集合
- public Collection<Session> getActiveSessions() {
- System.out.println("==============getActiveSessions=================");
- return redisTemplate.keys("*");
- }
-
- @Override// 加入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// 读取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;
- }
- }
SessionDao实现完了之后,我们就需要将SessionDao加入SessionManager中了,代码如下:
- @Bean
- public DefaultWebSessionManager configWebSessionManager(){
- DefaultWebSessionManager manager = new DefaultWebSessionManager();
- manager.setCacheManager(cacheManager);// 加入缓存管理器
- manager.setSessionDAO(sessionDao);// 设置SessionDao
- manager.setDeleteInvalidSessions(true);// 删除过期的session
- manager.setGlobalSessionTimeout(sessionDao.getExpireTime());// 设置全局session超时时间
- manager.setSessionValidationSchedulerEnabled(true);// 是否定时检查session
-
- return manager;
- }
最后一步就是将SessionManager配置到SecurityManager中了
- @Bean
- public SecurityManager securityManager(DefaultWebSessionManager webSessionManager) {
- DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
- // 设置realm.
- securityManager.setRealm(myShiroRealm());
-
- // 注入缓存管理器;
- securityManager.setCacheManager(cacheManager);// 这个如果执行多次,也是同样的一个对象;
-
- // session管理器
- securityManager.setSessionManager(webSessionManager);
-
- //注入记住我管理器;
- securityManager.setRememberMeManager(rememberMeManager());
- return securityManager;
- }
测试结果如下:
- ==============doReadSession=================
- ==============doReadSession=================
- ==============doReadSession=================
- ==============doReadSession=================
- ==============doReadSession=================
- ==============doReadSession=================
- ==============doReadSession=================
- ==============doReadSession=================
- ==============doReadSession=================
- ==============doReadSession=================
- ==============doReadSession=================
- ==============doReadSession=================
- ===============update================
- ==============doReadSession=================
- ==============doReadSession=================
- ===============update================
- ==============doReadSession=================
- ==============doReadSession=================
- ==============doReadSession=================
- 权限配置-->MyShiroRealm.doGetAuthorizationInfo()
- ==============doReadSession=================
我们会发现,当一个页面中存在多个资源的时候,会不停的调用doReadSession,update方法来读取和更新session,目前这个问题还没有想到比较好的解决方案。
|