4. JWT 载荷详解:注册声明、公共声明、私有声明

JWT 的第二部分——载荷(Payload)是承载实际数据的核心区域。它由三组声明(Claims)构成:注册声明、公共声明和私有声明。理解这三者的定义、用途和约束,是正确设计 JWT 认证与授权契约的基础。

4.1 注册声明(Registered Claims)

注册声明是 JWT 标准(RFC 7519)预定义的一组非强制性但强烈推荐的声明。它们提供了互操作性,确保不同系统之间能够理解相同的语义。以下是七个核心注册声明:

声明名 全称 类型 说明
iss Issuer 字符串或 URI 签发该 JWT 的主体。通常是服务端的唯一标识,如 https://auth.example.com。
sub Subject 字符串或 URI JWT 所描述的主体,通常是用户 ID 或唯一标识。在整个发行商范围内唯一。
aud Audience 字符串或数组 JWT 的接收方。标识该令牌预期给谁使用,可以是单个服务名或一组服务。
exp Expiration Time NumericDate 过期时间。在此时间之后,JWT 不得被接受处理。格式为 Unix 时间戳。
nbf Not Before NumericDate 生效时间。在此时间之前,JWT 不得被接受处理。
iat Issued At NumericDate 签发时间。标识 JWT 是什么时候被创建的。
jti JWT ID 字符串 令牌的唯一标识符。用于防止重放攻击,服务端可记录已使用的 jti。
实战建议:在微服务架构中,aud 声明尤其重要。例如,用户服务签发的令牌应明确指定 aud: ["order-service", "payment-service"],防止令牌被非预期的服务滥用。

4.2 公共声明(Public Claims)

公共声明是由 IANA JSON Web Token Claims 注册表定义的声明,或者使用 URI 作为名称以避免冲突。它们通常用于传递标准化的业务信息,例如用户角色、权限范围等。

  • 标准化公共声明:如 scope(权限范围)、client_id(客户端标识)等,已在 IANA 注册。
  • 自定义公共声明:使用 URI 格式命名,例如 https://api.example.com/claims/role,确保全球唯一性。
关键区别:公共声明与私有声明的核心区别在于命名空间的管理。公共声明通过注册或 URI 避免冲突,而私有声明仅在双方之间约定,不保证全局唯一。

4.3 私有声明(Private Claims)

私有声明是 JWT 签发方和接收方之间私下约定的声明。它们不经过注册,也不使用 URI 命名空间。私有声明适合传递内部业务数据,例如用户昵称、部门 ID、临时会话状态等。

{
  "sub": "user_12345",
  "iss": "auth.example.com",
  "exp": 1718000000,
  "iat": 1717913600,
  "role": "admin",          // 私有声明
  "department": "engineering", // 私有声明
  "session_id": "abc-123-def"  // 私有声明
}
安全警告:私有声明不应包含敏感信息(如密码、信用卡号),因为 JWT 载荷仅经过 Base64 编码,并非加密。任何拥有令牌的人都可以解码查看。

4.4 声明使用对比

维度 注册声明 公共声明 私有声明
命名方式 预定义三字符名称 IANA 注册或 URI 双方约定名称
全局唯一性 是 是(通过注册或 URI) 否
适用场景 令牌生命周期管理 标准化业务属性 内部业务数据
互操作性 高 中 低(仅限约定双方)
示例 exp, sub scope, https://example.com/role nickname, org_id

4.5 声明校验流程

服务端在接收到 JWT 后,必须对关键注册声明进行校验,以确保令牌的有效性和安全性。以下是标准校验步骤:

  1. 校验 exp:当前时间必须早于 exp 值,否则令牌已过期。
  2. 校验 nbf:当前时间必须晚于或等于 nbf 值,否则令牌尚未生效。
  3. 校验 iss:签发者必须与信任列表中的值匹配。
  4. 校验 aud:当前服务必须出现在 aud 列表中。
  5. 校验 jti:检查该 jti 是否已被使用(防重放)。
校验顺序建议:先校验 exp 和 nbf(时间相关),再校验 iss 和 aud(身份相关),最后校验 jti(唯一性相关)。这样可以尽早拒绝无效令牌,减少计算开销。
JWT 载荷声明分类与校验流程 注册声明 公共声明 私有声明 iss sub aud exp nbf iat jti 校验 exp 校验 nbf 校验 iss 校验 aud 校验 jti 校验顺序建议 先时间 → 再身份 → 后唯一性 尽早拒绝无效令牌 安全提醒 私有声明不加密 禁止存放敏感数据

4.6 实战案例:构建一个完整的 JWT 载荷

以下是一个包含注册声明、公共声明和私有声明的完整 JWT 载荷示例,用于一个电商平台的订单服务:

{
  "iss": "https://auth.shop.example.com",
  "sub": "user_98765",
  "aud": ["order-service", "payment-service"],
  "exp": 1718096400,
  "nbf": 1717913600,
  "iat": 1717913600,
  "jti": "unique-token-id-20240601",
  "scope": "read:orders write:orders",
  "https://shop.example.com/claims/role": "premium_member",
  "nickname": "张三",
  "department_id": "dept_ord_01"
}
解析:
  • iss 和 aud 确保令牌只能由认证中心签发,且仅用于订单和支付服务。
  • exp 和 nbf 控制令牌的有效时间窗口。
  • jti 用于防止重放攻击,服务端需维护已使用列表。
  • scope(公共声明)定义权限范围。
  • nickname 和 department_id(私有声明)传递内部业务数据。

4.7 常见误区与避坑指南

  • 误区一:认为所有声明都是必须的。实际上,只有 exp 在大多数安全场景中被强烈推荐,其他声明按需使用。
  • 误区二:在私有声明中存放密码或密钥。JWT 载荷仅 Base64 编码,任何持有令牌的人都可以解码查看。
  • 误区三:忽略 aud 校验。在多服务架构中,不校验 aud 可能导致令牌被非预期服务滥用。
  • 误区四:使用 iat 代替 nbf 做生效控制。iat 仅表示签发时间,不应作为生效判断依据。
关键提醒:始终在服务端对 exp、nbf、iss、aud 进行严格校验。忽略其中任何一个,都可能导致安全漏洞或业务逻辑错误。