使用Shiro+Redis实现Session共享

1. 为什么要实现Session共享?

说到这个问题,我们先来了解一下负载均衡的概念吧

1.1 负载均衡

当一个项目的访问量很大时,一个Tomcat吃不消,这时候就准备多个Tomcat,由Nginx按照权重来对请求进行分配,从而缓解单独一个Tomcat受到的压力。

如果想进一步了解的话,可以访问网站:Nginx 负载均衡

1.2 负载均衡中的Session问题

通过负载均衡,我们可以把请求分发到不同的 Tomcat 来缓解服务器的压力。但这存在一个问题:当同一个用户第一次访问tomcat:8080,并且登录成功;接着第二次访问时,却被分配到了tomcat:8081, 这里并没有记录他的登陆状态,那么就会呈现未登录状态了,严重影响了用户体验。

1.3 案例演示

或许这样说,比较抽象,就拿这篇博客-------一步一步地教你使用SpringBoot集成Shiro的代码演示吧。

在IDEA中,使用8080和8081端口启动项目。
对同一个SpringBoot项目使用不同端口启动,只需要配置VM参数即可:

D.server.port=端口号

按如下步骤:
1)、Run -> Edit Configurations
在这里插入图片描述
2)、填写VM参数
在这里插入图片描述
3)、点击左上角“+”,添加一个新的端口号
在这里插入图片描述
4)、填写名称,选择主类,填写VM参数
在这里插入图片描述
分别启动这两个端口的项目。

  1. 使用8080端口登录:http://localhost:8080/login
  2. 登录成功后,使用8081端口访问登录成功界面:http://localhost:8081/success
  3. 发现8081端口又跳转到了login页面,说明8081端口未登录

上面的问题:我在8080端口登录成功,访问8081端口,发现它没有登录。这就出现了上面的Session问题了。

那么,如何实现8080端口登录成功,8081端口也同时完成登录呢?

解决方法:

  1. ip_hash:通过ip地址标记用户,如果多次请求都是从同一个ip来的,那么就都分配到同一个tomcat。但这也有缺陷:如果tomcat:8080挂了,那么此时nginx只能把请求交给tomcat:8081,但是这里却没有记录session,用户体验依然受影响
  2. 使用redis:当tomcat:8080需要保存session值的时候,就可以把它放在Redis上,需要取的时候,也从Redis上取

2. Shiro架构

shiro的整体架构:
在这里插入图片描述
上面与会话或缓存相关的组件有:

  • Session Manager:会话管理器
  • Session DAO:会话 DAO,将session保存到数据库、缓存等
  • Cache Manager:缓存管理器,权限认证的缓存、用户及权限信息的缓存等

如果只是做Session共享,只需添加SessionDAO即可。不过为了防止出现缓存不一致性的问题,也将数据一起共享。所以,需要添加以上三个组件。

3. Shiro集成Redis

可查看:shiro-redis整合文档

在这篇博客----一步一步地教你使用SpringBoot集成Shiro 代码的基础上进行修改

1)、POM依赖

<dependency>
    <groupId>org.crazycake</groupId>
    <artifactId>shiro-redis</artifactId>
    <version>3.2.3</version>
</dependency>

2)、ShiroConfig
添加四个Bean

@Bean
public RedisManager redisManager() {
	// 它可对redis的ip、端口等进行配置
    RedisManager redisManager = new RedisManager();
    return redisManager;
}

@Bean
public SessionDAO sessionDAO() {
    RedisSessionDAO redisSessionDAO = new RedisSessionDAO();
    redisSessionDAO.setRedisManager(redisManager());
    // session在redis中的保存时间,最好大于session会话超时时间
    redisSessionDAO.setExpire(100);
    return redisSessionDAO;
}

@Bean
public RedisCacheManager cacheManager() {
    RedisCacheManager redisCacheManager = new RedisCacheManager();
    redisCacheManager.setRedisManager(redisManager());
    //redis中针对不同用户缓存
    redisCacheManager.setPrincipalIdFieldName("id");
    //用户权限信息缓存时间
    redisCacheManager.setExpire(200);
    return redisCacheManager;
}

@Bean
public SessionManager sessionManager() {
    DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
    sessionManager.setSessionDAO(sessionDAO());
    sessionManager.setCacheManager(cacheManager());
    // 局会话超时时间(单位毫秒),默认30分钟
    sessionManager.setGlobalSessionTimeout(10000);
    // 取消登录成功后url 后面的 JSESSIONID
    sessionManager.setSessionIdUrlRewritingEnabled(false);
    return sessionManager;
}

修改SecurityManager

@Bean
public SecurityManager securityManager() {
    DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
    securityManager.setRealm(userRealm());
    securityManager.setSessionManager(sessionManager());
    securityManager.setCacheManager(cacheManager());
    return securityManager;
}

3)、User类要实现Serializable接口,否则启动报错
4)、先启动redis,再启动两个端口项目
5)、使用8080端口登录,使用8081端口访问
8080端口:localhost:8080/login
在这里插入图片描述
8080端口登录成功
在这里插入图片描述
redis中也是有数据的

8081端口:http://localhost:8081/success
在这里插入图片描述
可直接访问,并访问成功。

好了,就这样实现了一个简单的Session共享了。

【参考文章】
SpringBoot shiro集成redis实现session共享

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章