2、JWT结构解析:Header、Payload、Signature 与 Base64Url 编码

JWT(JSON Web Token)本质上是一个紧凑的、自包含的令牌,其结构由三个独立的部分通过“.”连接而成。理解这三部分的构成与编码方式,是进行契约校验的基础。

2.1 JWT 三段式结构总览

一个标准的 JWT 字符串格式如下:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.
SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

三个部分分别对应:Header(头部)、Payload(载荷)、Signature(签名)。每一部分都经过 Base64Url 编码,最终以紧凑的字符串形式传输。

核心认知:JWT 的“自包含”特性意味着所有必要信息(如用户身份、过期时间)都编码在 Payload 中,服务端无需查询数据库即可完成初步校验。
JWT 三段式结构拆解 JWT 三段式结构拆解 Header 算法 & 类型 Base64Url 编码 Payload 声明(Claims) Base64Url 编码 Signature 签名验证 防篡改 最终格式:Header.Payload.Signature

2.2 Header(头部)

Header 通常包含两个关键字段:令牌类型(typ)和签名算法(alg)。

字段 含义 示例值
typ 令牌类型,固定为 JWT "JWT"
alg 签名算法,如 HS256、RS256、ES256 "HS256"

示例 Header JSON:

{
  "alg": "HS256",
  "typ": "JWT"
}
安全警告:切勿在 Header 中使用 "alg": "none"。攻击者可能通过篡改算法字段绕过签名校验,导致严重安全漏洞。契约校验时必须强制检查 alg 是否为预期算法。

2.3 Payload(载荷)

Payload 是 JWT 的核心数据区,包含一组“声明”(Claims)。声明分为三类:

  • 注册声明(Registered Claims):预定义的标准字段,如 iss(签发者)、sub(主题)、aud(受众)、exp(过期时间)、nbf(生效时间)、iat(签发时间)、jti(唯一标识)。
  • 公共声明(Public Claims):由使用方自定义,需在 IANA JSON Web Token Claims 注册或使用命名空间避免冲突。
  • 私有声明(Private Claims):双方约定使用的自定义字段,如 role、permissions。

示例 Payload JSON:

{
  "sub": "1234567890",
  "name": "John Doe",
  "iat": 1516239022,
  "exp": 1516242622,
  "role": "admin"
}
最佳实践:Payload 仅存储非敏感信息(如用户 ID、角色)。切勿存放密码、信用卡号等敏感数据,因为 Payload 仅经过 Base64Url 编码,并未加密。

2.4 Signature(签名)

Signature 用于验证令牌的完整性和真实性。其生成过程如下:

  1. 将 Base64Url 编码后的 Header 和 Payload 用“.”连接。
  2. 使用 Header 中指定的算法(如 HMAC SHA256)和密钥(secret)对上述字符串进行签名。
  3. 将生成的签名进行 Base64Url 编码,附加到 Payload 之后。

伪代码示例:

// 伪代码:签名生成
header = base64urlEncode(headerJson)
payload = base64urlEncode(payloadJson)
signatureInput = header + "." + payload
signature = HMAC-SHA256(signatureInput, secret)
jwt = signatureInput + "." + base64urlEncode(signature)

2.5 Base64Url 编码原理

Base64Url 是标准 Base64 的变体,专为 URL 安全设计。两者的核心差异如下:

特性 标准 Base64 Base64Url
第 62 个字符 + -
第 63 个字符 / _
填充字符 = 去除 =
URL 安全性 需额外编码 + 和 / 直接可用,无需转义

编码过程示例(JavaScript):

// Base64Url 编码函数
function base64urlEncode(data) {
  // 先进行标准 Base64 编码
  let base64 = btoa(data);
  // 替换 + 为 -,/ 为 _,去除末尾的 =
  return base64.replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
}

// 解码函数
function base64urlDecode(str) {
  // 恢复标准 Base64 字符
  let base64 = str.replace(/-/g, '+').replace(/_/g, '/');
  // 补回填充字符
  while (base64.length % 4) {
    base64 += '=';
  }
  return atob(base64);
}
Base64Url 编码转换流程 Base64Url 编码转换流程 原始 JSON 数据 标准 Base64 含 + / = 字符替换 + → -   / → _   去掉 = Base64Url 编码结果 URL 安全,无填充 关键:Base64Url 不是加密,只是编码 任何人都可以解码查看内容

2.6 完整 JWT 解码示例

以下是一个完整的 JWT 解码过程,展示三段式结构如何还原为可读数据:

// 原始 JWT
const jwt = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c";

// 解码 Header
const header = base64urlDecode("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9");
console.log(header); // {"alg":"HS256","typ":"JWT"}

// 解码 Payload
const payload = base64urlDecode("eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ");
console.log(payload); // {"sub":"1234567890","name":"John Doe","iat":1516239022}

// Signature 部分无法解码为可读 JSON,它是二进制签名数据的 Base64Url 表示
契约校验要点:在 JWT 契约校验中,服务端必须:① 解码 Header 确认 alg 符合预期;② 解码 Payload 提取声明并验证 exp、iss 等关键字段;③ 使用密钥重新计算签名,与 JWT 中的 Signature 比对,确保数据未被篡改。