Session 共享入门
1. 一句话简介
在传统 Servlet 容器中,HTTP Session 保存在单台 JVM 内存里,会带来"多实例不共享、重启即失效"两个痛点。Spring Session 通过引入 spring-session-data-redis,自动注册一个 SessionRepositoryFilter,在请求入口把 Servlet 容器原生 HttpSession 透明替换为基于 Redis 的实现,业务代码里的 session.setAttribute() / session.getAttribute() 无需任何修改即可读写 Redis。本模块演示了完整的经典效果:多实例部署时所有节点共享同一登录态,应用重启后 Session 依然存在、用户不用重新登录。
2. 什么时候使用
- ✅ 多实例部署,需要登录态共享:负载均衡下请求会落到不同实例,若 Session 各自存于 JVM 内存,用户在 A 实例登录后又被分到 B 实例就会掉线。把 Session 存进 Redis,所有实例共享同一份登录态,彻底解决共享问题。
- ✅ 需要应用发布重启不丢登录态:每次发版重启会清空 JVM 内的 Session,导致所有在线用户强制下线。Session 存于外部 Redis 后,重启不影响登录状态,本模块的验证步骤正是"重启程序后刷新首页仍显示 token、不跳登录页"。
- ✅ 对侵入性要求低、希望零改造:Spring Session 只替换底层 Session 实现,
HttpServletRequest.getSession()的调用方式、Session 的存值取值 API 完全不变,仅在 pom 增加依赖与几行配置即可接入。 - ✅ 已有 Redis 基础设施、希望一技术多用:Spring Session 支持 Redis、JDBC、Hazelcast、MongoDB 等后端,若体系内已部署 Redis,直接选用
spring-session-data-redis,既能做缓存又能存 Session,无需新增中间件。 - ❌ 单机单实例的小应用:只有一个节点且很少重启,引入 Redis 与 Spring Session 属于过度设计,原生内存 Session 更简单。
- ❌ 对极端性能极敏感的场景:Session 读写从 JVM 内存改为走网络访问 Redis,单次多一次网络往返;且
flush-mode: immediate(逐次实时写入)与on_save(请求结束批量写入)之间需要在实时性与写频率上做权衡。 - ❌ 强安全缓存/敏感数据:Session 往往明文序列化存入 Redis,Redis 若未做网络隔离与加密,存在被窃取登录态的风险,需谨慎处理安全加固。
- ❌ 访问量极大的纯会话状态系统:所有 Session 都压到单一 Redis 上会成为性能与单点瓶颈,虽可扩展,但对容量规划、Redis 高可用提出了明确的运维要求。
3. 常见业务场景
登录态共享:用户在任意后端节点完成登录后,把登录 token(本模块用 IdUtil.fastUUID() 生成的 UUID)写入 Session,所有实例都能读到同一份登录数据,实现"一次登录、集群通用"。本模块 doLogin 方法向 Session 写入 token,随后任意访问都命中已登录状态,正是这一场景的最小落地。
应用发版无感重启:系统发布新版本必然重启应用,原生 Session 会全部丢失。把 Session 放 Redis 后,重启期间浏览器 Cookie 与 Redis 中的会话数据都保持不变,用户刷新即可继续使用,无需重新登录。本模块的核心验证就是"重启程序后 Session 不失效"。
登录拦截与访问控制:借助 HandlerInterceptorAdapter 在请求进入 Controller 前检查 Session 中是否有登录凭证,无凭证则重定向到登录页。本模块的 SessionInterceptor 即校验 Session 中是否存在 SESSION_KEY 对应的 token,未登录统一 sendRedirect 到 /page/login?redirect=true,配合 WebMvcConfigurer 的路径排除实现"默认全拦截、登录页放行"的白名单策略。
多应用 Session 隔离(命名空间化):当多个应用共用同一套 Redis 时,通过 spring.session.redis.namespace 给不同应用的 Session 加不同前缀(本模块为 spring:session),避免不同系统覆盖彼此的会话数据,实现逻辑隔离下的共享基础设施复用。
多级缓存/无状态化改造辅助:将状态的载体从 JVM 迁移到 Redis 后,应用实例本身变"无状态",可水平随意伸缩、滚动更新,横向扩展不再受 Session 粘滞绑定限制,为实现真正的弹性扩缩容奠定基础。
4. 同类技术对比
| 维度 | Spring Session + Redis | 原生 Servlet HttpSession | Session 粘滞(Sticky Session) | JWT 无状态 Token |
|---|---|---|---|---|
| Session 存储 | 外部 Redis,独立于应用 | JVM 内存 | JVM 内存(各实例各自存) | 客户端,服务端无状态 |
| 多实例共享 | 天然支持,所有实例共享同一 Redis | 不支持 | 依赖负载均衡把同一用户固定到同一节点 | 天然支持,任何节点可验证 |
| 应用重启 | 不丢失,Redis 中数据保留 | 全部丢失,需重新登录 | 重启该节点即丢失 | 不丢失 |
| 登录失效控制 | 服务端可主动销毁/过期 | 依赖容器管理 | 依赖容器管理 | 需依赖黑名单/短 TTL,较难主动撤销 |
| 无状态性 | 中等,实例仍依赖 Redis,但可无状态化 | 有状态 | 有状态 | 完全无状态,可任意扩缩容 |
| 数据安全 | 明文落入 Redis,需安全加固 | 在 JVM 内部,不外露 | 在 JVM 内部 | Token 需防被篡改/破解,密钥需妥善保管 |
| 侵入性与成本 | 低侵入,pom 加依赖 + 几行配置 | 零配置 | 依赖网关/负载均衡配置 | 需改认证与请求解析逻辑,侵入较高 |
| 适用规模 | 多实例中型 Web 应用 | 单机小应用 | 中小型但需保证粘滞的集群 | 微服务、前后端分离、跨域跨服务 |
选型建议:多实例部署且希望登录态共享、发版不重启掉线的传统 Web 应用,首选 Spring Session + Redis,它在侵入性、无状态化与实现成本之间最均衡,是分布式 Session 的事实标准;单机小应用直接用原生 HttpSession,避免引入中间件;已有网关注入能力、分片压力主要在单点 Redis 上且能接受节点内失忆的旧系统,可退而求其次用 Sticky Session;面向微服务、前后端分离、需要跨服务无状态认证与水平无限扩缩容的新架构,则优先 JWT 这类无状态 Token,付出的代价是登录态主动撤销较难。需要说明的是,JWT 是"认证载体"、Spring Session 是"会话存储",二者并非完全同类,选择时还应结合已有的认证体系(如 Spring Security)一并考量。