4. JWT 基础:概念、结构与工作流程
JWT(JSON Web Token)是目前分布式系统中最主流的无状态认证方案。它通过自包含的令牌在客户端与服务端之间安全传递用户身份与声明信息,无需在服务端维护会话状态。本章从概念、结构到完整工作流程,为你建立扎实的 JWT 知识基础。
4.1 JWT 核心概念
JWT 是一种基于 JSON 的开放标准(RFC 7519),用于在各方之间以紧凑、自包含的方式安全传输信息。其核心特性包括:
- 紧凑(Compact):令牌体积小,可通过 URL、POST 参数或 HTTP Header 快速传输。
- 自包含(Self-contained):令牌本身携带用户身份、权限、过期时间等所有必要信息,服务端无需查询数据库即可完成验证。
- 无状态(Stateless):服务端不存储会话信息,天然适合水平扩展与微服务架构。
- 防篡改(Tamper-proof):通过数字签名保证令牌内容在传输过程中未被修改。
关键区别:传统 Session 认证将用户状态存储在服务端内存或 Redis 中,每次请求都需要查询会话数据;JWT 将状态编码进令牌本身,服务端只需验证签名即可信任令牌内容。
4.2 JWT 结构详解
一个完整的 JWT 由三个部分组成,以点号(.)分隔:Header.Payload.Signature。下面通过表格和图示详细拆解每一部分。
| 部分 | 名称 | 内容 | 编码方式 | 是否加密 |
|---|---|---|---|---|
| 第一部分 | Header(头部) | 令牌类型(typ)和签名算法(alg) | Base64URL | 否(明文) |
| 第二部分 | Payload(载荷) | 声明(Claims):用户信息、权限、过期时间等 | Base64URL | 否(明文) |
| 第三部分 | Signature(签名) | 对前两部分进行数字签名后的摘要 | Base64URL | 是(防篡改) |
4.2.1 Header(头部)
Header 通常包含两个字段:
{
"alg": "HS256",
"typ": "JWT"
}
- alg:签名算法,常见值有
HS256(HMAC-SHA256)、RS256(RSA-SHA256)、ES256(ECDSA-SHA256)。 - typ:令牌类型,固定为
JWT。
4.2.2 Payload(载荷)
Payload 是 JWT 的核心数据区,包含三类声明(Claims):
| 声明类型 | 说明 | 示例字段 |
|---|---|---|
| 注册声明(Registered Claims) | 预定义的标准字段,建议使用但不强制 | iss(签发者)、sub(主题)、exp(过期时间)、iat(签发时间) |
| 公共声明(Public Claims) | 自定义字段,需在 IANA 注册或使用命名空间避免冲突 | role、permissions |
| 私有声明(Private Claims) | 双方约定的自定义字段,不注册 | user_id、tenant_id |
{
"sub": "1234567890",
"name": "张三",
"role": "admin",
"iat": 1718000000,
"exp": 1718086400
}
安全警告:Payload 仅经过 Base64URL 编码,并未加密。任何持有令牌的人都可以解码查看内容。切勿在 Payload 中存放密码、密钥、身份证号等敏感信息。如需加密,应使用 JWE(JSON Web Encryption)。
4.2.3 Signature(签名)
签名是对 Header 和 Payload 的完整性保护。生成过程如下:
// 伪代码:签名生成逻辑
signature = HMACSHA256(
base64UrlEncode(header) + "." + base64UrlEncode(payload),
secret
)
服务端使用只有自己知道的密钥(对称算法)或私钥(非对称算法)生成签名。客户端无法伪造签名,因为不知道密钥。服务端验证时,使用相同算法和密钥重新计算签名,比对是否一致。
4.3 JWT 工作流程
下图展示了 JWT 在典型 Web 应用中的完整生命周期:
4.4 完整工作流程步骤
- 用户登录:客户端向认证服务提交用户名和密码。
- 身份验证:认证服务验证凭证,确认用户身份。
- 生成 JWT:认证服务构建 Header 和 Payload,使用密钥生成签名,组合成完整 JWT 令牌。
- 返回令牌:认证服务将 JWT 返回给客户端。
- 客户端存储:客户端将 JWT 存储在 localStorage、sessionStorage 或 Cookie 中。
- 携带令牌请求:客户端在后续请求的 HTTP Header 中添加
Authorization: Bearer <JWT>。 - 服务端验证:业务服务从 Header 中提取 JWT,验证签名完整性,检查
exp等声明是否有效。 - 提取用户信息:验证通过后,从 Payload 中读取用户身份和权限,处理业务逻辑。
- 返回响应:服务端将处理结果返回给客户端。
4.5 JWT 与 Session 对比
| 对比维度 | Session 认证 | JWT 认证 |
|---|---|---|
| 状态存储 | 服务端存储会话数据 | 客户端存储令牌,服务端无状态 |
| 扩展性 | 需要共享 Session 存储(如 Redis) | 天然支持水平扩展 |
| 跨域支持 | 需要额外配置 CORS 或代理 | 天然支持跨域 |
| 吊销机制 | 直接删除 Session 即可 | 需要黑名单或短过期 + 刷新令牌 |
| 令牌体积 | 仅 Session ID(小) | 包含完整声明(较大) |
| 安全性 | 依赖 Cookie 安全属性 | 依赖签名算法和密钥管理 |
选型建议:单体应用或需要即时吊销的场景优先选择 Session;微服务架构、移动端、前后端分离项目优先选择 JWT。生产环境中常将两者结合:使用短过期 JWT(15 分钟)配合刷新令牌(Refresh Token)实现安全与体验的平衡。
4.6 Go 语言中的 JWT 结构表示
在 Go 中,JWT 的三个部分可以映射为以下数据结构:
// Header 结构
type Header struct {
Alg string `json:"alg"` // 签名算法,如 "HS256"
Typ string `json:"typ"` // 令牌类型,固定 "JWT"
}
// Payload 结构(自定义声明)
type CustomClaims struct {
UserID int64 `json:"user_id"`
Username string `json:"username"`
Role string `json:"role"`
Perms []string `json:"permissions"`
// 注册声明
Iss string `json:"iss"` // 签发者
Sub string `json:"sub"` // 主题
Iat int64 `json:"iat"` // 签发时间
Exp int64 `json:"exp"` // 过期时间
}
// 完整 JWT 令牌(字符串形式)
// "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoxMjMsInJvbGUiOiJhZG1pbiJ9.abc123signature"