先记住这个答案
Session 认证将会话状态存于服务端,客户端仅保存不透明的 ID,天然可主动失效,但多实例需集中存储,扩展成本高。JWT 自包含用户信息与过期时间,服务端无需保存状态,便于水平扩展,但主动登出难,因为已签发令牌在过期前持续有效。取舍时,若要求快速吊销、强管控选 Session;若追求分布式友好、无状态可接受短期令牌,可选 JWT 并配合刷新令牌。
- Session 状态在服务端,JWT 状态在令牌本身
- JWT 更利水平扩展,但吊销难
- 需要强制下线时选 Session 或短时 JWT 加黑名单
状态存储与服务端视角差异
Session 机制下,用户登录后服务端生成唯一会话 ID,并将会话数据(如用户 ID、角色)存入服务端内存、Redis 或数据库中,同时把 ID 通过 Cookie 传给浏览器。后续请求携带该 ID,服务端查到对应状态,达到认证与授权。整个过程强调服务端完全可控,可随时修改或删除会话。
JWT 机制中,登录成功后服务端签发一个包含用户身份、权限和过期时间的自包含令牌,通常存放于浏览器 localStorage 或 HttpOnly Cookie。每次请求带上令牌,服务端验签后即可信任令牌内容,无需查询任何状态。但正因如此,只要令牌未过期,服务端无法主动令其失效,除非额外维护黑名单或缩短有效期。
多实例水平扩展下的选型
假设一个电商系统从单机迁移到 Kubernetes 多副本,原先 Session 存内存,负载均衡可能将同一用户的请求分发到不同 Pod,导致时常掉线。为修复必须引入共享存储如 Redis 会话存储,每个请求多一次网络开销,且 Redis 高可用需投入运维。
若改用 JWT,服务端不再需要共享状态,任意实例仅凭验签即可放行,扩容只是加 Pod。但因此带来一个实际取舍:当用户修改密码或账号被禁用时,旧 Token 在最多 15 分钟内仍可访问敏感接口。团队最终采用 JWT 并设置 15 分钟短 Access Token,配合刷新令牌实现有条件的主动登出。
适用边界与失效条件
Session 方案在单体或小规模集群中简单直接,但一旦流量暴涨需要快速水平扩容,共享存储的延迟和故障转移会成瓶颈。若内存存储不共享,则只能依赖粘性会话(Sticky Session),但粘性会加重单点负载,节点故障时丢状态。
JWT 方案有效的前提是资源服务器信任签发方且令牌有效期短。若业务要求即时吊销,如“管理员封号后立刻不能访问”,JWT 无状态优势不再,必须引入黑名单,黑名单要么存 Redis 使请求变有状态,要么压缩到本地缓存但多实例不同步。最终,极端安全场景还需回归服务端会话或令牌撤销列表。
容易答错的地方
- JWT 比 Session 更安全
- 两者安全性取决于实现。JWT 若存储于 Cookie,则可能受 CSRF 影响,需配合 SameSite 等机制;若存储于 localStorage,则主要面临 XSS 窃取风险。Session 常基于 Cookie,同样需注意 CSRF,可通过 SameSite 缓解。没有绝对安全。
- JWT 可以主动登出
- 如果仅删除客户端令牌,服务端签发的令牌仍有效,攻击者盗取后仍能使用。真正主动登出需服务端维护黑名单或缩短过期时间,这使 JWT 失去无状态优势,违背初衷。
面试官还会怎么问?
Refresh Token 与 Session 有何本质不同?
Refresh Token 是附带状态的长期凭证,服务端可吊销,类似 Session;Access Token 是无状态短时凭证。两者组合既支持扩展又能主动撤销,但复杂度高于纯 Session。
纯 JWT 应用如何实现紧急封禁?
常用黑名单表,将需撤销的 JTI 存入 Redis,每次请求检查。或用短时令牌等待自然过期,但封禁延迟不可控。也可用版本号声明,校验用户版本,改密后版本变更使旧令牌失效。
为什么 Session ID 必须随机不可预测?
若可预测,攻击者可猜出他人会话 ID 劫持会话。需使用 CSPRNG 生成至少 64 位熵的随机数,建议使用更长(如 128 位)以增强安全性,且登录后重新生成以防固定攻击。
参考资料
示例用于理解所注明的运行环境与边界;延伸学习可结合原文中的更多案例。