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

一、核心概念速览

1. Cookie

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 的签名。


二、三者的本质区别

维度CookieSessionJWT
存储位置客户端服务端客户端
是否有状态无状态(存储介质)有状态无状态
数据可见性客户端可见客户端不可见客户端可见(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 配置示例

@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 + RedisJWT
新增服务器需要配置 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 TokenHttpOnly Cookie7天有状态(可撤销),用于无感刷新

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


七、总结与选型建议

一句话总结

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

选型参考

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

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

text

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

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

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐