分布式会话
问题
分布式系统中如何管理用户会话?Session 共享有哪些方案?JWT 和 Session 各有什么优缺点?
答案
为什么需要分布式会话
单机部署时,Session 存在服务器内存中。集群部署后,用户请求可能被负载均衡分发到不同节点,导致 Session 丢失。
解决方案
方案一:Session 粘性(Sticky Session)
负载均衡器将同一用户的请求始终路由到同一节点。
Nginx ip_hash
upstream backend {
ip_hash;
server 192.168.1.1:8080;
server 192.168.1.2:8080;
}
| 优点 | 缺点 |
|---|---|
| 简单,不需要改代码 | 节点宕机 Session 丢失 |
| 无额外组件 | 负载不均匀 |
方案二:Session 集中存储(Redis)
所有节点共享一个 Redis 存储 Session,最主流的方案。
Spring Session + Redis
// 添加依赖 spring-session-data-redis
// application.yml
// spring:
// session:
// store-type: redis
// timeout: 30m
// redis:
// host: localhost
// port: 6379
Spring Session 自动将 Session 存到 Redis,对业务完全透明。
| 优点 | 缺点 |
|---|---|
| Session 共享,任意节点可访问 | 依赖 Redis,增加延迟 |
| 节点宕机不影响会话 | Redis 不可用则全部失效 |
方案三:JWT 无状态方案
服务端不存储 Session,将用户信息编码在 JWT Token 中,客户端每次请求携带 Token。
JWT 工具类
public class JwtUtil {
private static final String SECRET = "your-256-bit-secret";
public static String generateToken(Long userId, String role) {
return Jwts.builder()
.setSubject(userId.toString())
.claim("role", role)
.setIssuedAt(new Date())
.setExpiration(new Date(System.currentTimeMillis() + 3600000))
.signWith(SignatureAlgorithm.HS256, SECRET)
.compact();
}
public static Claims parseToken(String token) {
return Jwts.parser()
.setSigningKey(SECRET)
.parseClaimsJws(token)
.getBody();
}
}
方案对比
| 维度 | Session + Redis | JWT |
|---|---|---|
| 状态 | 有状态(服务端存储) | 无状态(客户端存储) |
| 扩展性 | 需要共享存储 | 天然支持分布式 |
| 安全性 | 较高(服务端控制) | 中等(无法主动失效) |
| 踢人/注销 | 直接删除 Session | 需要黑名单机制 |
| 存储 | Redis 内存占用 | Token 体积大,带宽开销 |
| 适用 | 传统 Web 应用 | 微服务、API、移动端 |
JWT 的主要问题
- 无法主动失效:Token 签发后,在过期前无法让它失效(退出登录需要黑名单)
- Token 续期:需要额外的 Refresh Token 机制
- 体积大:携带用户信息的 JWT 可能有几百字节
- 敏感信息泄露:Payload 是 Base64 编码(非加密),不要存敏感数据
常见面试问题
Q1: JWT 和 Session 怎么选?
答案:
- 传统 Web 应用(SSR):Session + Redis,能主动管理用户状态(踢人、强制下线)
- 微服务 / API / 移动端:JWT,无状态、跨服务方便
- 混合方案:JWT 做认证 + Redis 做会话状态管理(如在线状态)
大多数项目选 JWT + Refresh Token + Redis 黑名单。
Q2: JWT 如何实现登出?
答案:
JWT 本身无法撤销,常见方案:
- Redis 黑名单:登出时将 Token(或 jti)加入 Redis 黑名单,设置 TTL 为 Token 剩余有效期
- 短过期 + Refresh Token:Access Token 15 分钟过期,登出时在 Redis 中删除 Refresh Token
- 版本号:用户信息中维护 token_version,登出时 +1,验证时对比版本号
Q3: 分布式环境下如何实现单点登录(SSO)?
答案:
SSO 让用户在一个系统登录后,访问其他系统无需再次登录:
- CAS 模式:独立的认证中心,通过 Ticket 实现跨系统认证
- OAuth 2.0 / OIDC:通过授权码流程获取 Token
- 共享 Token:同一域名下共享 Cookie + Redis Session;跨域用 Token 方案
前端系列中有更详细的讨论,参见前端模块 JWT 认证 和 设计单点登录系统。