Session、Cookie、JWT 详解:从单体到分布式的认证方案演进

这篇文章适合刚接触 Web 认证机制的新手,也适合想系统梳理 Session、Cookie、JWT 知识的开发者。我会从三者的定义、存储位置、生命周期出发,结合单体架构、分布式架构的演进,讲清楚它们的区别与适用场景。

一、核心概念速览

Cookie 是服务器发送给浏览器并保存在客户端的一小段文本数据。

属性 说明
存储位置 浏览器客户端(SQLite 数据库或文件)
存储内容 键值对数据,如 JSESSIONID=ABC123
大小限制 单个 Cookie 约 4KB
生命周期 ExpiresMax-Age 控制;未设置则为会话级 Cookie,浏览器关闭即失效
传输方式 通过 HTTP 请求头的 Cookie 字段自动携带;服务器通过 Set-Cookie 响应头设置
安全特性 可设置 HttpOnly(防 XSS 读取)、Secure(仅 HTTPS)、SameSite(防 CSRF)

Cookie 本身只是"存储介质",不解决认证问题。它最常见的用途是承载 SessionID。


2. Session

Session 是服务器端存储用户会话状态的内存/存储对象。

属性 说明
存储位置 服务器端(Tomcat 的 ConcurrentHashMap、Redis、数据库等)
存储内容 用户身份信息、权限、业务上下文等敏感数据
大小限制 受服务器内存或存储限制
生命周期 由服务器控制(默认 30 分钟超时);可手动销毁
传输方式 客户端只持有 SessionID,实际数据不经过网络传输
安全特性 数据不暴露给客户端,安全性高

Session 是"有状态"的认证方案------服务器需要记住每个用户。


3. JWT(JSON Web Token)

JWT 是一种无状态的、自包含的 Token 格式标准。

属性 说明
存储位置 客户端(localStorage / sessionStorage / Cookie / 内存)
存储内容 Header(元数据)+ Payload(用户信息)+ Signature(签名)
大小限制 比 Cookie 大,通常几 KB
生命周期 由 Payload 中的 exp(过期时间)字段控制
传输方式 通常放在 Authorization: Bearer <token> 请求头中
安全特性 Payload 仅 Base64Url 编码(非加密),不能存敏感信息;签名防止篡改

JWT 是"无状态"的认证方案------服务器不存储会话信息,只负责验证 Token 的签名。


二、三者的本质区别

维度 Cookie Session JWT
存储位置 客户端 服务端 客户端
是否有状态 无状态(存储介质) 有状态 无状态
数据可见性 客户端可见 客户端不可见 客户端可见(Base64Url 编码)
安全性 中(可被篡改/窃取) 高(数据不暴露) 中(需签名验证 + HTTPS)
扩展性 不涉及 分布式需共享存储 天然适合分布式
主动撤销 支持(删除/覆盖) 支持(删除 Session) 困难(需黑名单机制)

三、Session + Cookie:单体架构下的典型方案

工作流程

【用户登录】 浏览器 → POST /login ↓ Tomcat 执行 request.getSession() ↓ Tomcat 内部创建 Session

  • 生成 SessionID: "ABC123"

  • 存入 ConcurrentHashMap: { "ABC123": {userId: 1, role: "ADMIN"} }

  • 创建 Cookie: JSESSIONID=ABC123 ↓ 浏览器 ← Set-Cookie: JSESSIONID=ABC123; HttpOnly

【后续请求】 浏览器 → GET /user/profile Cookie: JSESSIONID=ABC123 ↓ Tomcat 解析 Cookie → 查找 sessions.get("ABC123") → 返回用户数据

text

复制代码
### 架构图

┌─────────────────────────────────────────────────┐

│ 浏览器 │

│ ┌──────────────────────────────────────────┐ │

│ │ Cookie: JSESSIONID=ABC123 │ │

│ └──────────────────────────────────────────┘ │

└─────────────────────────────────────────────────┘

│ 自动携带 Cookie

┌─────────────────────────────────────────────────┐

│ Tomcat 服务器 │

│ ┌──────────────────────────────────────────┐ │

│ │ ConcurrentHashMap │ │

│ │ ┌──────────────────────────────────┐ │ │

│ │ │ "ABC123" → {userId: 1, role: ADMIN}│ │ │

│ │ └──────────────────────────────────┘ │ │

│ └──────────────────────────────────────────┘ │

└─────────────────────────────────────────────────┘

优点

  • 实现简单,Tomcat/Undertow 等容器自动管理

  • 数据安全,用户无法直接访问 Session 内容

  • 支持主动踢人、强制下线等操作

缺点

  • 有状态,服务器需要存储每个在线用户

  • 分布式环境下 Session 不共享,需额外方案


四、Session + Redis:分布式架构的共享方案

当系统部署在多台服务器时,用户的请求可能被负载均衡到不同机器。Session 默认存储在各自的本地内存,导致用户登录后第二次请求落到另一台服务器时丢失会话。

解决方案:引入 Redis 作为共享 Session 存储

┌─────────────────────────────────────────────────────────┐

│ 浏览器 │

│ ┌──────────────────────────────────────────────────┐ │

│ │ Cookie: JSESSIONID=ABC123 │ │

│ └──────────────────────────────────────────────────┘ │

└─────────────────────────────────────────────────────────┘

│ 请求可能落到任意一台服务器

┌───────────────┼───────────────┐

▼ ▼ ▼

┌──────────────┐ ┌──────────────┐ ┌──────────────┐

│ Tomcat-1 │ │ Tomcat-2 │ │ Tomcat-3 │

│ 查 Session │ │ 查 Session │ │ 查 Session │

└──────┬───────┘ └──────┬───────┘ └──────┬───────┘

│ │ │

└────────────────┼────────────────┘

┌───────────────────────┐

│ Redis 集群 │

│ ┌──────────────────┐ │

│ │ "ABC123" → {...} │ │ ← 所有服务器共享

│ └──────────────────┘ │

└───────────────────────┘

Spring Boot 配置示例

java 复制代码
@Configuration
@EnableRedisHttpSession  // 启用 Redis 共享 Session
public class SessionConfig {
    // 所有服务器从 Redis 读写 Session,实现共享
}

特点

  • 解决了分布式 Session 共享问题

  • 但仍然是有状态的------Redis 存储了会话数据

  • 引入额外组件(Redis),增加运维复杂度

  • 每次请求都要查 Redis,有一定性能开销


五、JWT:彻底走向无状态的分布式方案

Session + Redis 虽然解决了共享问题,但本质还是"有状态"------服务器需要依赖 Redis 存储用户状态。

JWT 则把状态从服务器转移到了客户端

JWT 的结构

text

复制代码
eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMjMifQ.abc123xyz
       ↑                    ↑              ↑
    Header              Payload        Signature
   (元数据)            (用户信息)        (防篡改签名)
  • Header:声明算法和类型

  • Payload :存储用户信息(如 userIdroleexp 过期时间)

  • Signature:对前两部分签名,防止篡改

注:三段各自 Base64Url 编码,然后用 . 拼接,不是整体编码。

JWT 的工作流程

复制代码
【用户登录】
浏览器 → POST /login
         ↓
       Spring Boot 验证身份 → 生成 JWT
         - Header 转 JSON → Base64Url
         - Payload 转 JSON → Base64Url  
         - 拼接后用密钥签名
         ↓
浏览器 ← { "token": "eyJhbGciOi...eyJzdWIi...abc123" }
​
【后续请求】
浏览器 → GET /user/profile
         Authorization: Bearer eyJhbGciOi...eyJzdWIi...abc123
         ↓
       任意服务器 → 验证签名 → 解析 Payload → 获取 userId
         - 无需查 Redis/数据库
         - 无需共享存储

分布式架构下的 JWT 优势

复制代码
┌─────────────────────────────────────────────────────────┐
│                   浏览器                                │
│  ┌──────────────────────────────────────────────────┐  │
│  │  存储 JWT(localStorage / 内存 / Cookie)        │  │
│  └──────────────────────────────────────────────────┘  │
└─────────────────────────────────────────────────────────┘
                        │
                        │ 请求携带 JWT(请求头)
                        ▼
        ┌───────────────┼───────────────┐
        ▼               ▼               ▼
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│  服务器-1    │ │  服务器-2    │ │  服务器-3    │
│  验证签名    │ │  验证签名    │ │  验证签名    │
│  解析用户    │ │  解析用户    │ │  解析用户    │
└──────────────┘ └──────────────┘ └──────────────┘
        │               │               │
        └───────────────┴───────────────┘
                        │
            无需共享存储,每台服务器独立工作

为什么 JWT 适合分布式?

场景 Session + Redis JWT
新增服务器 需要配置 Redis 连接 直接加入集群,无需额外配置
服务器重启 Redis 中 Session 仍在 无影响(服务器不存储状态)
请求处理 每次查 Redis 只做签名运算(CPU),无 I/O
扩展性 受 Redis 连接数限制 水平扩展无瓶颈

六、最佳实践:双 Token 机制

生产环境中,通常采用 Access Token(短有效期)+ Refresh Token(长有效期) 的组合方案:

text

复制代码
用户登录
    ↓
签发 Access Token(15分钟有效期)→ 存在内存中
    ↓
签发 Refresh Token(7天有效期)→ 存在 HttpOnly Cookie 中
    ↓
日常请求:携带 Access Token
    ↓
Access Token 过期 → 用 Refresh Token 换新的 Access Token
Token 类型 存储位置 有效期 特点
Access Token 内存 15分钟 无状态,短命,降低泄露风险
Refresh Token HttpOnly Cookie 7天 有状态(可撤销),用于无感刷新

这种方案结合了无状态 API 的性能优势和主动撤销的安全管控能力。


七、总结与选型建议

一句话总结

Cookie 是客户端的存储容器,Session 是服务端的有状态会话,JWT 是客户端的无状态凭证。

选型参考

应用场景 推荐方案 理由
单体应用 / 后台管理系统 Session + Cookie 实现简单,容器自动管理
分布式应用(多服务器) Session + Redis 共享会话,保留主动控制能力
微服务 / 前后端分离 JWT 无状态,天然适合分布式
移动端 / 第三方 API JWT 跨平台,无 Cookie 依赖
高安全要求应用 JWT + Refresh Token 短生命周期 + 可撤销

核心思考:有状态 vs 无状态

text

复制代码
有状态(Session):服务器说"我记得你"
    - 优点:数据安全、可主动撤销
    - 缺点:需要存储、分布式需共享
    
无状态(JWT):服务器说"我不记得你,但你的凭证说明了你是谁"
    - 优点:无需存储、天然分布式友好
    - 缺点:无法主动撤销、Token 需管理

没有绝对的好坏,只有是否适合当前场景。理解三者的本质,才能在设计系统架构时做出正确的选择。

相关推荐
Database_Cool_2 小时前
阿里云 PolarDB-X vs OceanBase 国产分布式数据库深度对比
分布式·阿里云·oceanbase
她说可以呀2 小时前
RabbitMQ重试机制
分布式·rabbitmq
wuqingshun3141592 小时前
RabbitMQ 中消息什么时候会进入死信交换机?
分布式·rabbitmq
ClickHouseDB4 小时前
推出 ClickHouse Cloud 中的多阶段分布式查询执行
分布式·clickhouse
BD_Marathon12 小时前
spark.sql报错
大数据·分布式·spark
长不胖的路人甲21 小时前
RabbitMQ 死信队列 DLQ
分布式·rabbitmq
可乐ea21 小时前
【Redis八股|第8篇】Redis 分布式锁原理与 Redisson 使用
数据库·redis·分布式·面试题·redis八股
逸Y 仙X1 天前
Spark部署
大数据·分布式·spark
BD_Marathon1 天前
什么是Spark
大数据·分布式·spark