精品推荐

  • 国内稀缺优秀Java全栈课程-Vue+SpringBoot通讯录系统全新发布!

  • Docker快速手上视频教程(无废话版)【免费】

作者:夜月归途

转载自:

https://www.cnblogs.com/guitu18/p/11262106.html

本篇是Shiro系列第二篇,使用Shiro基于Redis实现分布式环境下的Session共享。在讲Session共享之前先说一下为什么要做Session共享。

首发地址:https://www.guitu18.com/post/2019/07/28/44.html


为什么要做Session共享

什么是Session

我们都知道HTTP协议(1.1)是无状态的,所以服务器在需要识别用户访问的时候,就要做相应的记录用于跟踪用户操作,这个实现机制就是Session。当一个用户第一次访问服务器的时候,服务器就会为用户创建一个Session,每个Session都有一个唯一的SessionId(应用级别)用于标识用户。

Session通常不会单独出现,因为请求是无状态的,那么我们必须让用户在下次请求时带上服务器为其生成的Session的ID,通常的做法时使用Cookie实现(当然你要非要在请求参数中带上SessionId那也不是不行)。请求返回时会向浏览器的Cookie中写入SessionID,通常使用的键是JSESSIONID,这样下次用户再请求这台服务器时,服务器就能从Cookie中取出SessionId识别出该次请求的用户是谁。

举个栗子:

左边红框部分是Cookie列表,当前服务器是:localhost:28080。右边红框部分从左到右依次是Cookie的键、值、主机、路径和过期时间。路径为/时表示全站有效,最后一个过期时间未设置的话是默认值为Session,表示浏览器关闭时该Cookie失效。我们也可以为Cookie指定过期时间,以做到会话保持。

什么是Session共享

通过Session和Cookie,我们使得无状态的HTTP协议间接的变成了有状态的了,可以实现保持登录,存储用户信息,购物车等等功能。但是随着服务访问人数的增多,单台服务器已经不足以应付所有的请求了,必须部署集群环境。但是随着集群环境的出现,追踪用户状态的问题又开始出现问题,之前用户在A服务器登录,A服务器保存了用户信息,但是下一次请求发送到B服务器去了,这时候B服务器是不知道用户在A服务器登录的事情的,它虽然也能拿到用户请求Cookie中的SessionId,但是在B服务根据这个SessionId找不到对应的Session,B服务器就会认为用户没有登录,需要用户重新登录,这对用户来说是没办法接受的。

这时候常见的有两种方式解决这个问题,第一种是让这个用户所有的请求都发送到A服务器,比如根据IP地址做一些列算法将所有用户分配到不同的服务器上去,让每个用户只访问其中的一台服务器。这种做法可行,但是后续也会产生其它问题,更好的做法是第二种,将所有的服务器上的Session都做成共享的,A服务能拿到B服务器上的所有Session,同理B服务器也能获取A服务器所有的Session,这样上面的问题就不存在了。


Shiro结合Redis实现Session共享

上一篇已经通过Shiro实现了用户登录和权限管理,Shiro的登录也是基于Session的,默认情况下Session是保存在内存中。既然要做Session共享,那么肯定是将Session抽取出来,放到一个多个服务器都能访问到的地方。

在集群环境下,我们仅仅需要继承AbstractSessionDAO,实现一下Session的增删改查等几个方法就可以很方便的实现Session共享,Shiro已经将完整的流程都做好了。这里涉及到的设计模式是模板方法模式,我们仅需要参与部分业务就可以完善整个流程了,当然我们不参与这部分流程的话,Shiro也有默认的实现方式,那就是将Session管理在当前应用的内存中。

具体的Session管理(共享)怎么实现由我们自己决定,可以存放在数据库,也可以通过网络传输,甚至可以通过IO流写入文件都行,但就性能来讲,我们一般都将Session放入Redis中。Redis大法好!YES~

自定义RedisSessionDAO

理解了原理之后就很容易办事了,继承AbstractSessionDAO后实现Session增删改查的几个方法,然后再分布式系统中所有的项目再需要存储或获取Session时都会走Redis操作,这样就做到了集群环境的Session共享了。代码非常简单:

@Componentpublic class RedisSessionDao extends AbstractSessionDAO {    @Value("${session.redis.expireTime}")    private long expireTime;    @Autowired    private RedisTemplate redisTemplate;    @Override    protected Serializable doCreate(Session session) {        Serializable sessionId = this.generateSessionId(session);        this.assignSessionId(session, sessionId);        redisTemplate.opsForValue().set(session.getId(), session, expireTime, TimeUnit.SECONDS);        return sessionId;    }    @Override    protected Session doReadSession(Serializable sessionId) {        return sessionId == null ? null : (Session) redisTemplate.opsForValue().get(sessionId);    }    @Override    public void update(Session session) throws UnknownSessionException {        if (session != null && session.getId() != null) {            session.setTimeout(expireTime * 1000);            redisTemplate.opsForValue().set(session.getId(), session, expireTime, TimeUnit.SECONDS);        }    }    @Override    public void delete(Session session) {        if (session != null && session.getId() != null) {            redisTemplate.opsForValue().getOperations().delete(session.getId());        }    }    @Override    public CollectiongetActiveSessions() {        return redisTemplate.keys("*");    }}

配置文件中添加上面用到的配置

###redis连接配置spring.redis.host=localhostspring.redis.port=6379spring.redis.password=foobared### Session过期时间(秒)session.redis.expireTime=3600

注入RedisSessionDao

上面只是我们自己实现的管理Session的方式,现在需要将其注入SessionManager中,并设置过期时间等相关参数。

@Bean    public DefaultWebSessionManager defaultWebSessionManager(RedisSessionDao redisSessionDao) {        DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();        sessionManager.setGlobalSessionTimeout(expireTime * 1000);        sessionManager.setDeleteInvalidSessions(true);        sessionManager.setSessionDAO(redisSessionDao);        sessionManager.setSessionValidationSchedulerEnabled(true);        sessionManager.setDeleteInvalidSessions(true);        /**         * 修改Cookie中的SessionId的key,默认为JSESSIONID,自定义名称         */        sessionManager.setSessionIdCookie(new SimpleCookie("JSESSIONID"));        return sessionManager;    }

再将SessionManager注入Shiro的安全管理器SecurityManager中,前面说过,我们围绕安全相关的所有操作,都需要与SecurityManager打交道,这位才是Shiro中真正的老大哥。

@Bean    public SecurityManager securityManager(UserAuthorizingRealm userRealm, RedisSessionDao redisSessionDao) {        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();        securityManager.setRealm(userRealm);        // 取消Cookie中的RememberMe参数        securityManager.setRememberMeManager(null);        securityManager.setSessionManager(defaultWebSessionManager(redisSessionDao));        return securityManager;    }

OK,至此基于Redis实现的Session共享就完成了,是不是简单得不可思议。

注意:基于网络传输的对象请实现Serializable序列化接口,比如User类。

测试

将这套代码用不同的端口跑两套服务(理论上跑多少套都可以只要你的配置够用),访问两台服务器获取用户信息的接口,未登录状态毫无疑问都会跳到登录页去:

在任意一台服务器上调用登录接口登录:

登录成功后再次分别访问两台服务器获取用户信息的接口:

如此,分布式环境Session共享完美实现。最后继续放上项目代码,代码还是很早之前的,部分代码为了配合此篇笔记经过修改整理后上传。

Gitee:https://gitee.com/guitu18/ShiroDemo

GitHub:https://github.com/guitu18/ShiroDemo


本篇结束,简直不要太简单是不是,其实这主要是因为大部分工作Shiro都帮我们做了,细节的东西都被Shiro隐藏起来,我们仅仅需要添加一些简单的配置就可以实现强大的功能,这就是框架的好处。

但是作为一个程序员,仅仅调用一个方法或者添加一个注解就实现了一套很强大的功能,而我们却看不到一个if判断和for循环的时候心里应该是非常不踏实的。我们不仅要学会使用框架,更要去深入理解框架,至少要知道为什么我们就加了一个注解框架就能帮我们实现一大堆功能,只有这样才能让我们感到脚踏实地。下一篇,深入Shiro源码看看,可能需要酝酿一下想想笔记怎么写。

C#session共享+redis_Shiro权限管理框架(二):Shiro结合Redis实现分布式环境下的Session共享...相关推荐

  1. 分布式环境下的session共享

    Session共享在当前这个互联网背景下,已经不是一个新鲜的话题了,而且如何解决session共享其实也有很多非常成熟的方案 服务器实现的session复制或session共享,这类型的共享sessi ...

  2. Spring-Redis实现分布式环境下主子域名Session共享

    背景: 之前一直采用通过注解的方式配置Spring环境下的子域名共享,其基本思路是通过将session放入redis中,然后将使用HTTPSESSION更改为使用SpringSession的方式,使得 ...

  3. mysql集群session_集群/分布式环境下5种session处理策略

    前言 在搭建完集群环境后,不得不考虑的一个问题就是用户访问产生的session如何处理.如果不做任何处理的话,用户将出现频繁登录的现象,比如集群中存在A.B两台服务器,用户在第一次访问网站时,Ngin ...

  4. 【Linux运维-集群技术进阶】集群/分布式环境下5种session处理策略

    前言 在搭建完集群环境后,不得不考虑的一个问题就是用户访问产生的session如何处理.如果不做任何处理的话,用户将出现频繁登录的现象,比如集群中存在A.B两台服务器,用户在第一次访问网站时,Ngin ...

  5. 集群/分布式环境下5种session处理策略

    前言 在搭建完集群环境后,不得不考虑的一个问题就是用户访问产生的session如何处理.如果不做任何处理的话,用户将出现频繁登录的现象,比如集群中存在A.B两台服务器,用户在第一次访问网站时,Ngin ...

  6. 面试官:分布式环境下,如何实现session共享

    点击关注公众号,实用技术文章及时了解 先了解一下为什么会出现这种session共享的解决方案? 随着互联网公司的项目在微服务和分布式的环境下进行的搭建,导致一个项目可能分别部署在几个甚至很多的服务器集 ...

  7. 分布式架构下,Session 共享有什么方案?

    来自:会点代码的大叔 分布式架构下的 Session 共享,也称作分布式 Session 一致性:分布式架构下 Session 共享有哪些问题,又有哪些解决方案,让我们一起看一下. 01 Sessio ...

  8. weblogic 12C集群环境下的session复制

    做过weblogic集群环境的人应该都清楚,要想实现session同步,必须满足两个条件:第一,在weblogic.xml里面增加session同步相关的代码:第二,所有放入session的类都要序列 ...

  9. SEO学习笔记二:在搜索引擎竞价排名环境下,个人网站将何去何从?

    本文首发于「妙蛙种子前端」博客,欢迎关注- 早期的搜索引擎,大家都在一个相对公平的规则内玩耍:你的内容够好,网站体验更优秀,在搜索引擎中的排名一般都会比较高. 因为搜索引擎能便捷的为我们定位到精准的内 ...

最新文章

  1. RTT设备与驱动之PWM
  2. Eclipse一直building workspace问题解决
  3. vscode pylint报错的问题
  4. JVM:永久代 以及jdk1.8为什么将其移除?
  5. JS事件及其兼容用法
  6. create new page group - ST05
  7. Spring Boot中使用模板引擎引用资源
  8. 【Python】一句话 if else 简洁写法
  9. 数据预处理之数据描述
  10. 南方科技大学计算机系师资,于仕琪 - 教师个人主页 - 南方科技大学
  11. 面试官:TCP和UDP两者的区别是什么?
  12. Struts2拦截器详解
  13. win11 22H2任务栏一条白线如何消除?
  14. sqlite3数据库函数
  15. Python爬虫---汽车之家字体反爬
  16. 九宫格数独游戏——回溯算法——java实现
  17. Spring——DI
  18. Visual Studio 2015/2017 与ASP.NET CORE 联合创建具有SPA模式的Angular2模板
  19. 饥荒联机云服务器_饥荒联机云服务器开档
  20. Java知识汇总——思维导图

热门文章

  1. vue2.0 子组件和父组件之间的传值
  2. JavaScript No Overloading 函数无重载之说
  3. 如何选择合适的数据库,让游戏更高效可用
  4. ESXI主机定时重启脚本
  5. MapInfo之格式说明(转载)
  6. IBM JDK,SUN JDK,BEA JDK区别在哪里?
  7. Pycharm连接远程服务器进行代码调试开发
  8. github上关于机器学习的awesome项目(资源大全)
  9. sklearn数据集变换
  10. 微信小程序_Bug解决_setData失效