3. JWT 数据结构详解
JWT(JSON Web Token)本质上是一个紧凑的、URL 安全的令牌表示方式,用于在各方之间以 JSON 对象的形式安全地传输信息。一个标准的 JWT 由三个独立的部分组成,以点号(.)分隔:Header、Payload、Signature。其结构表现为:xxxxx.yyyyy.zzzzz。
核心记忆点:JWT 的每一部分都是 Base64URL 编码后的 JSON 字符串。Base64URL 编码是标准 Base64 的变体,将
+ 替换为 -,将 / 替换为 _,并去除末尾的 =,使其能够安全地放在 URL 中传输。
3.1 Header(头部)
Header 通常由两部分组成:令牌的类型(typ)和使用的签名算法(alg)。它是 JWT 的第一段,用于描述 JWT 的元数据。
| 声明字段 | 含义 | 是否必选 | 示例值 |
|---|---|---|---|
alg |
签名算法,如 HMAC SHA256、RSA 等 | 是 | "HS256" |
typ |
令牌类型,固定为 "JWT" |
否(建议携带) | "JWT" |
cty |
内容类型,用于嵌套 JWT 场景 | 否 | "JWT" |
安全提示:在生产环境中,务必在服务端白名单中校验
alg 字段。攻击者可能将 alg 改为 "none" 来绕过签名验证,导致严重的安全漏洞。
// 一个典型的 Header 示例
{
"alg": "HS256",
"typ": "JWT"
}
// 经过 Base64URL 编码后变为:eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
3.2 Payload(载荷)
Payload 是 JWT 的第二部分,包含实际的声明(Claims)。声明是关于实体(通常是用户)和附加数据的陈述。JWT 规范定义了三种类型的声明:注册声明、公共声明和私有声明。
3.2.1 注册声明(Registered Claims)
这些是一组预定义的、非强制性的但推荐使用的声明,旨在提供一组有用的、可互操作的声明。
| 声明 | 全称 | 含义 | 类型 |
|---|---|---|---|
iss |
Issuer | 签发者,标识签发 JWT 的主体 | 字符串或 URI |
sub |
Subject | 主题,标识 JWT 所面向的用户(通常是用户 ID) | 字符串 |
aud |
Audience | 接收方,标识此 JWT 的预期接收者 | 字符串或数组 |
exp |
Expiration Time | 过期时间,必须晚于当前时间 | NumericDate(秒) |
nbf |
Not Before | 生效时间,在此时间之前 JWT 不可用 | NumericDate(秒) |
iat |
Issued At | 签发时间 | NumericDate(秒) |
jti |
JWT ID | 唯一标识符,用于防止重放攻击 | 字符串 |
关键校验点:服务端在验证 JWT 时,必须严格校验
exp(过期时间)和 nbf(生效时间)。如果当前时间不在 [nbf, exp] 区间内,应直接拒绝该令牌。同时,建议校验 iss 和 aud 是否与预期值匹配。
3.2.2 公共声明与私有声明
- 公共声明(Public Claims):可以在 IANA JSON Web Token Registry 中注册,或使用包含冲突避免命名空间的 URI 来定义。
- 私有声明(Private Claims):由使用 JWT 的双方协商定义,不属于注册声明或公共声明。例如:
"role": "admin"、"permissions": ["read", "write"]。
// 一个包含注册声明和私有声明的 Payload 示例
{
"iss": "https://auth.example.com",
"sub": "user_123456",
"aud": "https://api.example.com",
"exp": 1735689600,
"iat": 1735603200,
"jti": "unique-token-id-abc123",
"role": "admin",
"department": "engineering"
}
// 经过 Base64URL 编码后变为:eyJpc3MiOiJodHRwczovL2F1dGguZXhhbXBsZS5jb20iLCJzdWIiOiJ1c2VyXzEyMzQ1NiIsImF1ZCI6Imh0dHBzOi8vYXBpLmV4YW1wbGUuY29tIiwiZXhwIjoxNzM1Njg5NjAwLCJpYXQiOjE3MzU2MDMyMDAsImp0aSI6InVuaXF1ZS10b2tlbi1pZC1hYmMxMjMiLCJyb2xlIjoiYWRtaW4iLCJkZXBhcnRtZW50IjoiZW5naW5lZXJpbmcifQ
3.3 Signature(签名)
Signature 是 JWT 的第三部分,用于验证消息在传输过程中未被篡改。对于使用私钥签名的令牌,它还可以验证 JWT 的发送方是否为它所声称的实体。
3.3.1 签名生成公式
签名是通过对编码后的 Header 和 Payload 进行组合,并使用指定的算法和密钥进行加密计算得出的。
// 通用签名生成公式
signature = HMACSHA256(
base64UrlEncode(header) + "." + base64UrlEncode(payload),
secret
)
// 对于非对称算法(如 RS256)
signature = RSASHA256(
base64UrlEncode(header) + "." + base64UrlEncode(payload),
privateKey
)
3.3.2 签名验证流程
- 接收方从 JWT 中提取出 Header 和 Payload 的 Base64URL 编码部分。
- 使用相同的算法和密钥(或公钥)对
header.payload重新计算签名。 - 将计算出的签名与 JWT 中携带的签名进行比较。
- 如果两者完全一致,则说明 JWT 内容未被篡改;否则,拒绝该令牌。
3.4 完整 JWT 示例
将上述三个部分通过点号连接,即构成一个完整的 JWT 令牌。
// 完整 JWT 示例
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.
eyJpc3MiOiJodHRwczovL2F1dGguZXhhbXBsZS5jb20iLCJzdWIiOiJ1c2VyXzEyMzQ1NiIsImF1ZCI6Imh0dHBzOi8vYXBpLmV4YW1wbGUuY29tIiwiZXhwIjoxNzM1Njg5NjAwLCJpYXQiOjE3MzU2MDMyMDAsImp0aSI6InVuaXF1ZS10b2tlbi1pZC1hYmMxMjMiLCJyb2xlIjoiYWRtaW4iLCJkZXBhcnRtZW50IjoiZW5naW5lZXJpbmcifQ.
SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
重要提醒:JWT 的 Header 和 Payload 仅经过 Base64URL 编码,并未加密。任何持有 JWT 的人都可以解码并查看其内容。因此,切勿在 Payload 中存放密码、密钥等敏感信息。如果需要保护内容,应使用 JWE(JSON Web Encryption)对 Payload 进行加密。
3.5 常见算法对比
| 算法 | 类型 | 密钥 | 性能 | 适用场景 |
|---|---|---|---|---|
HS256 |
对称(HMAC) | 共享密钥 | 高 | 单一服务内部,或完全信任的微服务间 |
RS256 |
非对称(RSA) | 私钥签名 / 公钥验证 | 中 | 分布式系统、第三方应用、开放 API |
ES256 |
非对称(ECDSA) | 私钥签名 / 公钥验证 | 高(密钥更短) | 移动端、IoT 设备、对性能要求高的场景 |
选型建议:在分布式鉴权系统中,推荐使用非对称算法(如 RS256 或 ES256)。认证服务持有私钥签发令牌,其他业务服务仅需持有公钥即可验证令牌,无需共享密钥,大大降低了密钥泄露的风险。