后端工程(02):鉴权体系——Session/Cookie、JWT、OAuth2、OIDC、SSO
更新时间:2026-09-01。本文是
backend/engineering/后端工程第 02 篇,接 API 设计规范。鉴权是后端系统的核心基础设施。从 Session/Cookie 到 JWT 到 OAuth2,每个方案都有不同的适用场景。
本文要回答的问题
- Session/Cookie 认证怎么工作?有什么优缺点?
- JWT 的结构是什么?为什么比 Session 适合分布式?
- OAuth2 的授权码流程是什么?为什么需要授权码?
- OIDC 和 OAuth2 什么关系?SSO 单点登录怎么实现?
一、Session/Cookie 认证
http
# 登录流程
1. POST /login { username, password }
2. 服务端验证成功,创建 Session,返回 Session ID
3. 服务端设置 Cookie: session_id=abc123
4. 后续请求自动带 Cookie
# Session 存储位置
# 单机:内存
# 分布式:Redis(集中存储,所有节点共享)
# 工作流程
┌─────────┐ POST /login ┌─────────┐
│ 客户端 │ ──────────────────→ │ 服务端 │
│ │ Set-Cookie │ │
│ │ ←────────────────── │ 存储 Session│
│ │ GET /api/data │ │
│ │ Cookie: session_id │ │
│ │ ──────────────────→ │ 查询 Session│
│ │ data │ │
│ │ ←────────────────── │ │
└─────────┘ └─────────┘Session 优缺点:
- 优点:服务端控制,可以主动销毁(踢人),容易实现
- 缺点:分布式需要 Redis 存储,有状态,服务端扩容需要考虑 Session 共享
二、JWT(JSON Web Token)
text
# JWT 结构:header.payload.signature
# eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMjMifQ.abc123
# 1. Header:算法和类型
{
"alg": "HS256",
"typ": "JWT"
}
# 2. Payload:声明
{
"sub": "123", # 用户 ID
"name": "Alice", # 用户名
"iat": 1516239022, # 签发时间
"exp": 1516240022, # 过期时间
"role": "admin" # 角色(自定义)
}
# 3. Signature:防止篡改
HMAC-SHA256(
base64UrlEncode(header) + "." + base64UrlEncode(payload),
secret
)JWT 优缺点:
- 优点:无状态,不需要存储,适合分布式,支持跨域
- 缺点:无法主动销毁(签发后修改不了),jwt 大小比 session id 大
JWT 使用流程:
http
# 登录
POST /login → 返回 { "token": "eyJ..." }
# 请求
GET /api/data
Authorization: Bearer eyJ...
# 服务端验证:验证签名 + 过期时间 → 解析 payload三、JWT vs Session
| 对比 | Session | JWT |
|---|---|---|
| 存储 | 服务端存储(Redis/内存) | 客户端存储(token 本身) |
| 有状态 | 有状态,需要共享存储 | 无状态,不需要存储 |
| 主动销毁 | 可以,删除 Session | 不行,需要黑名单 |
| 分布式 | 需要 Redis 统一存储 | 天然支持,所有节点只需验证签名 |
| 大小 | 小(session id) | 大(payload 越大越大) |
| 安全 | Cookie 有 CSRF 风险 | 放在 Authorization 头,无 CSRF |
| 适合场景 | 传统 Web 应用 | 移动端、API 服务、微服务 |
经验:
- 传统 Web 应用(有页面):Session
- 移动端、微服务、API 服务:JWT
- 也可以组合:JWT + Redis 黑名单,兼顾无状态和可撤销
四、OAuth2 授权码流程
text
OAuth2 是一个授权框架,不是认证协议
核心角色:资源所有者(用户)、客户端(应用)、授权服务器、资源服务器
# 授权码流程(最完整,最常用)
1. 用户访问客户端,客户端重定向到授权服务器
GET /authorize?response_type=code&client_id=myapp&redirect_uri=https://app.com/callback
2. 用户授权(登录确认授权)
3. 授权服务器返回授权码,重定向到 redirect_uri
GET https://app.com/callback?code=abc123
4. 客户端用授权码 + client_secret 换取 access_token
POST /token
{ "grant_type": "authorization_code", "code": "abc123", "client_id": "myapp", "client_secret": "secret" }
5. 授权服务器返回 access_token(和 refresh_token)
{ "access_token": "eyJ...", "refresh_token": "def456", "expires_in": 3600 }
6. 客户端用 access_token 访问资源
GET /api/user
Authorization: Bearer eyJ...为什么需要授权码?
- 直接返回 access_token 给客户端,如果被拦截,坏人拿到 access_token
- 授权码只能使用一次,且 client_secret 只有客户端知道
- 授权码 + client_secret 才能换 access_token,更安全
五、OIDC(OpenID Connect)
text
OIDC = OAuth2 + 身份认证
OAuth2 只做授权(给访问权限),不做身份认证(你是谁)
OIDC 在 OAuth2 基础上加 id_token(JWT 格式),包含用户身份信息
# OIDC 的 id_token
{
"iss": "https://auth.example.com", # 签发者
"sub": "1234567890", # 用户 ID
"aud": "myapp", # 接收方
"exp": 1516239022, # 过期时间
"iat": 1516239022, # 签发时间
"name": "Alice", # 用户信息
"email": "alice@example.com"
}OIDC 流程: 同 OAuth2 授权码流程,但多了 scope=openid 和 id_token。
六、SSO(单点登录)
text
SSO 核心:一个登录,多个应用共用
# 实现方式
1. 共享 Session:多个应用共享同一个 Session 存储(Redis)
2. 基于 JWT:所有应用共享 JWT 签名密钥
3. 基于 OAuth2/OIDC:统一认证中心
# 流程
1. 用户访问应用 A → 未登录 → 重定向到认证中心
2. 用户登录认证中心 → 认证中心设置 Cookie(SSO 域)
3. 生成 token → 重定向回应用 A
4. 用户访问应用 B → 未登录 → 重定向到认证中心
5. 认证中心发现用户已登录(SSO Cookie)→ 直接生成 token → 重定向回应用 B
6. 用户不需要再次登录SSO 的挑战:
- 单点故障:认证中心挂了,所有应用都受影响
- 跨域 Cookie:需要共享 Cookie 域,子域名可以解决
- 登出:SSO 登出时需要通知所有应用
七、常见坑对照
| 坑 | 现象 | 对策 |
|---|---|---|
| JWT 不设过期时间 | token 永远有效 | 设置 exp 声明,用 refresh_token 刷新 |
| JWT 太大 | 每次请求携带大 token,浪费带宽 | payload 只放用户 ID 和角色,不放多余信息 |
| Session 没放 Redis | 重启后所有用户 logout | 用 Redis 存储 Session |
| OAuth2 授权码泄露 | 坏人拿到授权码,换 token | 授权码只能使用一次,加 PKCE 增强 |
相关与延伸
下一篇:消息中间件——Kafka/RabbitMQ 模型对比、可靠投递、消费组、延迟权衡;API 设计规范,见 API 设计规范。
一句话总结
鉴权体系:Session 有状态,服务端控制,分布式需 Redis 共享;JWT 无状态,自包含,适合 API 和微服务,但不可撤销;OAuth2 授权码流程:客户端 → 授权码 → access_token,client_secret 保证安全,授权码只能用一次;OIDC = OAuth2 + id_token,加身份认证;SSO 单点登录,一个认证中心多个应用共用,用户登录一次访问所有应用。