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

时间:2022-02-14 02:30:29

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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
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中了,代码如下:

?
1
2
3
4
5
6
7
8
9
10
11
@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中了

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@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,目前这个问题还没有想到比较好的解决方案。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持服务器之家。

原文链接:http://blog.csdn.net/liuchuanhong1/article/details/76690682