分享

spring boot整合redis实现shiro的分布式session共享

 WindySky 2017-10-25 发布于广东

我们知道,shiro是通过SessionManager来管理Session的,而对于Session的操作则是通过SessionDao来实现的,默认的情况下,shiro实现了两种SessionDao,分别为CachingSessionDAO和MemorySessionDAO,当我们使用EhCache缓存时,则是使用的CachingSessionDAO,不适用缓存的情况下,就会选择基于内存的SessionDao.所以,如果我们想实现基于Redis的分布式Session共享,重点在于重写SessionManager中的SessionDao。我们的重写代码如下:

  1. package com.chhliu.springboot.shiro.cache;  
  2.   
  3. import java.io.Serializable;  
  4. import java.util.Collection;  
  5. import java.util.concurrent.TimeUnit;  
  6.   
  7. import org.apache.shiro.session.Session;  
  8. import org.apache.shiro.session.UnknownSessionException;  
  9. import org.apache.shiro.session.mgt.eis.AbstractSessionDAO;  
  10. import org.springframework.beans.factory.annotation.Autowired;  
  11. import org.springframework.data.redis.core.RedisTemplate;  
  12. import org.springframework.stereotype.Service;  
  13.   
  14. @Service  
  15. @SuppressWarnings({ "rawtypes", "unchecked" })  
  16. public class RedisSessionDao extends AbstractSessionDAO {  
  17.   
  18.     // Session超时时间,单位为毫秒  
  19.     private long expireTime = 120000;  
  20.   
  21.     @Autowired  
  22.     private RedisTemplate redisTemplate;// Redis操作类,对这个使用不熟悉的,可以参考前面的博客  
  23.   
  24.     public RedisSessionDao() {  
  25.         super();  
  26.     }  
  27.   
  28.     public RedisSessionDao(long expireTime, RedisTemplate redisTemplate) {  
  29.         super();  
  30.         this.expireTime = expireTime;  
  31.         this.redisTemplate = redisTemplate;  
  32.     }  
  33.   
  34.     @Override // 更新session  
  35.     public void update(Session session) throws UnknownSessionException {  
  36.         System.out.println("===============update================");  
  37.         if (session == null || session.getId() == null) {  
  38.             return;  
  39.         }  
  40.         session.setTimeout(expireTime);  
  41.         redisTemplate.opsForValue().set(session.getId(), session, expireTime, TimeUnit.MILLISECONDS);  
  42.     }  
  43.   
  44.     @Override // 删除session  
  45.     public void delete(Session session) {  
  46.         System.out.println("===============delete================");  
  47.         if (null == session) {  
  48.             return;  
  49.         }  
  50.         redisTemplate.opsForValue().getOperations().delete(session.getId());  
  51.     }  
  52.   
  53.     @Override// 获取活跃的session,可以用来统计在线人数,如果要实现这个功能,可以在将session加入redis时指定一个session前缀,统计的时候则使用keys("session-prefix*")的方式来模糊查找redis中所有的session集合  
  54.     public Collection<Session> getActiveSessions() {  
  55.         System.out.println("==============getActiveSessions=================");  
  56.         return redisTemplate.keys("*");  
  57.     }  
  58.   
  59.     @Override// 加入session  
  60.     protected Serializable doCreate(Session session) {  
  61.         System.out.println("===============doCreate================");  
  62.         Serializable sessionId = this.generateSessionId(session);  
  63.         this.assignSessionId(session, sessionId);  
  64.   
  65.         redisTemplate.opsForValue().set(session.getId(), session, expireTime, TimeUnit.MILLISECONDS);  
  66.         return sessionId;  
  67.     }  
  68.   
  69.     @Override// 读取session  
  70.     protected Session doReadSession(Serializable sessionId) {  
  71.         System.out.println("==============doReadSession=================");  
  72.         if (sessionId == null) {  
  73.             return null;  
  74.         }  
  75.         return (Session) redisTemplate.opsForValue().get(sessionId);  
  76.     }  
  77.   
  78.     public long getExpireTime() {  
  79.         return expireTime;  
  80.     }  
  81.   
  82.     public void setExpireTime(long expireTime) {  
  83.         this.expireTime = expireTime;  
  84.     }  
  85.   
  86.     public RedisTemplate getRedisTemplate() {  
  87.         return redisTemplate;  
  88.     }  
  89.   
  90.     public void setRedisTemplate(RedisTemplate redisTemplate) {  
  91.         this.redisTemplate = redisTemplate;  
  92.     }  
  93. }  

SessionDao实现完了之后,我们就需要将SessionDao加入SessionManager中了,代码如下:

  1. @Bean  
  2.     public DefaultWebSessionManager configWebSessionManager(){  
  3.         DefaultWebSessionManager manager = new DefaultWebSessionManager();  
  4.         manager.setCacheManager(cacheManager);// 加入缓存管理器  
  5.         manager.setSessionDAO(sessionDao);// 设置SessionDao  
  6.         manager.setDeleteInvalidSessions(true);// 删除过期的session  
  7.         manager.setGlobalSessionTimeout(sessionDao.getExpireTime());// 设置全局session超时时间  
  8.         manager.setSessionValidationSchedulerEnabled(true);// 是否定时检查session  
  9.           
  10.         return manager;  
  11.     }  

最后一步就是将SessionManager配置到SecurityManager中了

  1. @Bean  
  2.     public SecurityManager securityManager(DefaultWebSessionManager webSessionManager) {  
  3.         DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();  
  4.         // 设置realm.  
  5.         securityManager.setRealm(myShiroRealm());  
  6.   
  7.         // 注入缓存管理器;  
  8.         securityManager.setCacheManager(cacheManager);// 这个如果执行多次,也是同样的一个对象;  
  9.           
  10.         // session管理器  
  11.         securityManager.setSessionManager(webSessionManager);  
  12.           
  13.         //注入记住我管理器;  
  14.         securityManager.setRememberMeManager(rememberMeManager());  
  15.         return securityManager;  
  16.     }  

测试结果如下:

  1. ==============doReadSession=================  
  2. ==============doReadSession=================  
  3. ==============doReadSession=================  
  4. ==============doReadSession=================  
  5. ==============doReadSession=================  
  6. ==============doReadSession=================  
  7. ==============doReadSession=================  
  8. ==============doReadSession=================  
  9. ==============doReadSession=================  
  10. ==============doReadSession=================  
  11. ==============doReadSession=================  
  12. ==============doReadSession=================  
  13. ===============update================  
  14. ==============doReadSession=================  
  15. ==============doReadSession=================  
  16. ===============update================  
  17. ==============doReadSession=================  
  18. ==============doReadSession=================  
  19. ==============doReadSession=================  
  20. 权限配置-->MyShiroRealm.doGetAuthorizationInfo()  
  21. ==============doReadSession=================  

我们会发现,当一个页面中存在多个资源的时候,会不停的调用doReadSession,update方法来读取和更新session,目前这个问题还没有想到比较好的解决方案。

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多