前一段时间,鹏哥接到了关于SpringSession改造的任务。任务的起因是原来项目是使用的oauth2 协议以及前后端分离的架构,对分布式Session的要求不是很强烈,但是项目上线的时候要对接客户原CAS服务,客户CAS 版本比较老,使用的还是CAS2.0的协议,在集群和前后端分离的场景下,分布式Session就变得必上不可了,因为同一个认证如果认证在微服务A节点上,如果没有使用分布式Session,那么在同一个架构下的微服务B节点就需要再认证一次(登录一次),这是客户万万不能接受的,所以才有了这个任务
鹏哥之前也没有Spring Session的基础,完全是从零开始,在搜索了无数教程和一步一步的跟踪代码之后,慢慢摸清了一些门道,分享给大家。知识点很多很杂,思路和流程主要参考一网友怀瑾握瑜的博客(https://www.cnblogs.com/lxyit/category/1303872.html),因为作者的思路还算清晰,鹏哥受益匪浅。
我们先开通过一张图来介绍一下SpringSession的原理:

spring-session分为以下核心模块:
- SessionRepositoryFilter:Servlet规范中Filter的实现,用来切换HttpSession至Spring Session,包装HttpServletRequest和HttpServletResponse
- HttpServerletRequest/HttpServletResponse/HttpSessionWrapper包装器:包装原有的HttpServletRequest、HttpServletResponse和Spring Session,实现切换Session和透明继承HttpSession的关键之所在
- Session:Spring Session模块
- SessionRepository:管理Spring Session的模块
- HttpSessionStrategy:映射HttpRequst和HttpResponse到Session的策略
好了看了原理的大概介绍,鹏哥来大家走一遍这个流程:Spring Session的入口是 @EnableRedisHttpSession ,那我们就从这里看起,这个类并没有什么特殊的代码,定义了几个默认属性,唯一意义重大的是引入了 RedisHttpSessionConfiguration ,而这个类才是Spring Session的核心:

我们发现在 RedisHttpSessionConfiguration 几乎配置了Spring Session 的所有行为

在这个配置文件中,配置了Spring Session 的核心模块 RedisOperationsSessionRepository

初始化了Session事件监听器MessageListener模块

spring-session在启动时监听Redis的channel,使用Redis的键空间通知处理Session的删除和过期事件和使用Pub/Sub模式处理Session创建事件
关于RedisSession的存储管理部分已经初始化,但是spring-session的另一个基础设施模块SessionRepositoryFilter是在RedisHttpSessionConfiguration父类SpringHttpSessionConfiguration中初始化。

回顾思考spring-boot自动配置spring-session,其实也是大部分Spring Boot模块的配置层级:
- SpringHttpSessionConfiguration是spring-session本身的配置类,与spring-boot无关,毕竟spring-session也可以整合单纯的spring项目,只需要使用该spring-session的配置类即可。
- RedisHttpSessionConfiguration用于配置spring-session的Redission,毕竟spring-session还支持其他的各种session:Map/JDBC/MogonDB等,将其从SpringHttpSessionConfiguration隔离开来,遵循开闭原则和接口隔离原则。但是其必须依赖基础的SpringHttpSessionConfiguration,所以使用了继承。RedisHttpSessionConfiguration是spring-session和spring-data-redis整合配置,需要依赖spring-data-redis。
- SpringBootRedisHttpSessionConfiguration才是spring-boot中关键配置
- RedisSessionConfiguration主要用于处理自定义配置,将application.yml或者application.properties的配置载入。