4. JWT Payload 详解:注册声明、公共声明、私有声明
JWT 的 Payload(载荷)是令牌中承载实际数据的部分,它是一个 JSON 对象,包含一组声明(Claims)。声明是关于实体(通常是用户)和附加数据的陈述。根据 JWT 标准(RFC 7519),声明分为三种类型:注册声明、公共声明和私有声明。理解这三者的边界与用途,是设计安全、可互操作的 JWT 认证授权系统的核心。
4.1 注册声明(Registered Claims)
注册声明是一组预定义的、非强制性的但推荐使用的声明。它们提供了互操作性基础,确保不同系统之间对某些关键字段有一致的理解。这些声明通常用于控制令牌的生命周期和标识。
| 声明名 | 全称 | 类型 | 说明 |
|---|---|---|---|
| iss | Issuer | 字符串或 URL | 签发该 JWT 的实体。通常是服务端的唯一标识,如 https://auth.example.com。接收方应验证 iss 是否与期望的签发者一致。 |
| sub | Subject | 字符串 | JWT 所描述的主体。通常是用户的唯一标识(如 user_id)。该声明在签发者范围内必须是全局唯一的。 |
| aud | Audience | 字符串或数组 | JWT 的接收方。标识此令牌 intended for 哪个服务。如果接收方不在 aud 列表中,应拒绝该令牌。 |
| exp | Expiration Time | 数字(NumericDate) | 过期时间。在此时间之后,JWT 不能再被接受处理。这是一个绝对时间戳(Unix 时间戳)。 |
| nbf | Not Before | 数字(NumericDate) | 生效时间。在此时间之前,JWT 不能被接受处理。用于延迟令牌生效。 |
| iat | Issued At | 数字(NumericDate) | 签发时间。标识 JWT 是什么时候被创建的。 |
| jti | JWT ID | 字符串 | 令牌的唯一标识符。用于防止重放攻击(Replay Attack)。服务端可以维护一个已使用 jti 的集合。 |
最佳实践: 始终验证
exp 和 iss。对于跨服务调用的场景,务必验证 aud。对于高安全性场景,使用 jti 并配合服务端缓存实现一次性令牌。
4.2 公共声明(Public Claims)
公共声明是由 IANA JSON Web Token Claims 注册表定义的,或者使用 URI(统一资源标识符)定义的声明,以避免命名冲突。这些声明通常用于传递标准化的、非敏感的用户属性或上下文信息。
- 标准化公共声明示例:
name、email、picture、preferred_username等。这些字段在 OpenID Connect 标准中被广泛使用。 - 自定义公共声明: 如果你的组织需要定义一个新的声明,并且希望它能在多个系统间共享,建议使用一个包含命名空间的 URI 作为声明名,例如
https://yourcompany.com/claims/employee_id。
安全警告: 公共声明通常包含用户个人信息(PII)。切勿将密码、密钥、完整的信用卡号等敏感数据放入 JWT 的 Payload 中。JWT 的 Payload 默认只经过 Base64 编码,并未加密,任何拥有令牌的人都可以解码读取。
4.3 私有声明(Private Claims)
私有声明是 JWT 的签发者和接收者之间私下约定的声明。它们既不是注册声明,也不是公共声明。使用私有声明时,双方必须事先就声明名称和值的含义达成一致。
- 典型用途: 传递应用特定的业务数据,如用户角色(
role)、权限列表(permissions)、部门 ID(dept_id)等。 - 命名冲突风险: 由于没有全局注册机制,私有声明名应尽量具体,避免使用过于通用的名称(如
data、info),以防止与未来的注册声明或公共声明冲突。
4.4 三种声明的对比与选择
| 特性 | 注册声明 | 公共声明 | 私有声明 |
|---|---|---|---|
| 标准化程度 | 高(RFC 标准) | 中(IANA 注册或 URI) | 低(双方约定) |
| 命名冲突风险 | 低 | 低(使用 URI 时) | 高 |
| 互操作性 | 强 | 较强 | 弱(仅限约定双方) |
| 典型用途 | 生命周期控制、标识 | 标准化用户信息 | 应用特定业务数据 |
4.5 Payload 结构示例
以下是一个包含注册声明、公共声明和私有声明的完整 JWT Payload 示例:
{
// 注册声明
"iss": "https://auth.myapp.com",
"sub": "user_1234567890",
"aud": ["https://api.myapp.com", "https://admin.myapp.com"],
"exp": 1718000000,
"nbf": 1717996400,
"iat": 1717996400,
"jti": "unique-token-id-abc123",
// 公共声明(标准化)
"name": "张三",
"email": "zhangsan@example.com",
// 私有声明(应用特定)
"role": "admin",
"permissions": ["read:users", "write:users", "delete:users"],
"dept_id": "DEPT-IT-001"
}
4.6 Payload 声明分类架构图
下图展示了 JWT Payload 中三类声明的分类、来源及其在令牌中的位置关系:
4.7 声明验证流程
接收方在解析 JWT 时,应按照以下流程验证 Payload 中的声明,以确保令牌的合法性和有效性:
- 验证签名: 确保 JWT 未被篡改(在验证 Header 和 Payload 之前,这是第一步)。
- 验证
iss: 检查签发者是否在受信任的签发者列表中。 - 验证
aud: 检查当前服务是否在令牌的接收方列表中。 - 验证
exp: 检查当前时间是否已超过过期时间。 - 验证
nbf: 如果存在,检查当前时间是否在生效时间之后。 - 验证
jti: 如果启用了防重放,检查该 jti 是否已被使用。 - 提取业务声明: 验证通过后,提取公共声明和私有声明用于授权决策。
核心要点: Payload 是 JWT 的“数据面”,其设计直接决定了系统的安全性和可扩展性。注册声明是骨架,公共声明是标准接口,私有声明是业务血肉。三者结合,构成了 JWT 认证授权契约的完整数据边界。