2. JWT 生成原理:Header 与 Payload 的 Base64Url 编码、Signature 的 HMAC/RSA/ECDSA 算法原理

JWT(JSON Web Token)的结构由三个独立的部分组成,通过点号(.)连接:Header、Payload、Signature。理解每一部分的生成原理,是排查令牌过期、签名校验失败等安全合规错误的基础。

2.1 JWT 三段式结构总览

一个完整的 JWT 字符串形如:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.
SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

下表展示了三段结构的名称、编码方式与核心作用:

段位 名称 编码方式 核心作用
第一段 Header(头部) Base64Url 编码 声明令牌类型(JWT)与签名算法
第二段 Payload(载荷) Base64Url 编码 携带声明(Claims),如用户身份、过期时间
第三段 Signature(签名) 算法生成(非编码) 防篡改验证,确保 Header 与 Payload 未被修改
JWT 三段式结构生成流程 Header {"alg":"HS256","typ":"JWT"} Payload {"sub":"123","exp":1700000000} Signature HMAC-SHA256(base64UrlEncode) 最终输出:Header.Base64Url(Payload).Signature 三段通过点号(.)连接,形成完整的 JWT 令牌字符串

2.2 Header 与 Payload 的 Base64Url 编码

Base64Url 编码是标准 Base64 的变体,专为 URL 安全场景设计。JWT 的 Header 和 Payload 在签名前必须经过此编码。

2.2.1 编码差异对比

特性 标准 Base64 Base64Url(JWT 使用)
填充字符 = 去除(No padding)
第 62 位字符 + -(减号)
第 63 位字符 / _(下划线)
URL 传输 需额外转义 直接安全传输

2.2.2 Header 生成步骤

  1. 构造 JSON 对象:必须包含 alg(算法)和 typ(类型,固定为 "JWT")。
  2. 序列化为 JSON 字符串:紧凑格式,无多余空格。
  3. Base64Url 编码:将 JSON 字节流编码,去除尾部 =,替换 + 为 -,/ 为 _。
// 示例:Header JSON
{"alg":"HS256","typ":"JWT"}
// Base64Url 编码后
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9

2.2.3 Payload 生成步骤

  1. 构造 Claims 集合:包含注册声明(iss、sub、exp、iat 等)、公共声明、私有声明。
  2. 序列化为 JSON 字符串:注意 exp(过期时间)为 Unix 时间戳。
  3. Base64Url 编码:与 Header 相同的编码规则。
⚠️ 安全警示:Base64Url 编码不是加密!Payload 中的任何信息(如用户 ID、角色)都是明文可读的。绝不要在 Payload 中存放密码、密钥等敏感数据。

2.3 Signature 的算法原理

签名是 JWT 防篡改的核心。根据 Header 中 alg 字段的声明,签名算法分为三类:HMAC(对称)、RSA(非对称)、ECDSA(椭圆曲线非对称)。

2.3.1 签名生成通用公式

Signature = Algorithm(
  base64UrlEncode(Header) + "." + base64UrlEncode(Payload),
  secretOrPrivateKey
)

签名输入是前两段编码后的字符串(含中间点号),加上密钥。输出为二进制签名数据,再经过 Base64Url 编码得到第三段。

2.3.2 三种算法对比

算法族 典型 alg 值 密钥类型 验证方 性能 适用场景
HMAC HS256、HS384、HS512 对称密钥(同一个密钥签名和验证) 持有相同密钥的服务端 极快 单体服务、内部微服务
RSA RS256、RS384、RS512 非对称(私钥签名,公钥验证) 持有公钥的任何方 较慢(签名慢) 开放 API、第三方分发
ECDSA ES256、ES384、ES512 非对称(私钥签名,公钥验证) 持有公钥的任何方 快(密钥更短) 移动端、IoT、高性能要求

2.3.3 HMAC 算法详解(以 HS256 为例)

HMAC-SHA256 使用一个对称密钥对消息进行哈希运算,生成固定长度的摘要。

// 伪代码示例
signature = HMAC-SHA256(
  base64UrlEncode(header) + "." + base64UrlEncode(payload),
  secretKey
)
// 输出二进制,再 Base64Url 编码得到第三段
  • 密钥要求:至少与哈希输出长度相同(HS256 建议 256 位/32 字节)。
  • 安全风险:密钥泄露则所有令牌可被伪造。必须通过环境变量或密钥管理服务(KMS)保管。

2.3.4 RSA 算法详解(以 RS256 为例)

RSA 使用私钥签名,公钥验证。签名过程涉及对消息哈希的加密操作。

// 伪代码示例
signature = RSA-SHA256(
  base64UrlEncode(header) + "." + base64UrlEncode(payload),
  privateKey
)
// 验证方使用公钥解密签名,比对哈希值
  • 密钥对生成:推荐 2048 位或以上。
  • 优势:公钥可安全分发给任意客户端或第三方服务,私钥仅存于签发服务。
  • 性能:签名操作比 HMAC 慢 10-100 倍,验证相对较快。

2.3.5 ECDSA 算法详解(以 ES256 为例)

ECDSA 基于椭圆曲线密码学,使用 P-256 曲线(secp256r1)。

// 伪代码示例
signature = ECDSA-SHA256(
  base64UrlEncode(header) + "." + base64UrlEncode(payload),
  privateKey
)
// 输出为 R + S 两个大整数的 DER 编码或原始连接格式
  • 密钥长度:P-256 私钥仅 32 字节,公钥 64 字节(未压缩)。
  • 优势:同等安全强度下,密钥更短、计算更快,适合资源受限环境。
  • 注意:不同库对 ECDSA 签名的编码格式(DER vs. raw R||S)可能不同,需保持兼容。
三种签名算法验证流程对比 HMAC(对称) 同一密钥 签名 = 验证 密钥不泄露即安全 RSA(非对称) 私钥签名 公钥验证 公钥可公开分发 ECDSA(椭圆曲线) 私钥签名 公钥验证 密钥更短,性能更优

2.4 常见故障排查要点

🔍 故障排查清单:
  • Base64Url 填充错误:某些库自动补全 =,某些不补。验证时需兼容两种格式。
  • 算法混淆:签发时用 HS256,验证时误用 RS256,导致签名校验失败。
  • 密钥不匹配:HMAC 密钥长度不足、RSA 公钥与私钥不配对、ECDSA 曲线参数不一致。
  • 签名输入格式:确保签名输入为 header.payload(含点号),不含多余空格或换行。

2.5 完整生成示例(HS256)

// 1. Header
{"alg":"HS256","typ":"JWT"}
// Base64Url: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9

// 2. Payload
{"sub":"user123","exp":1710000000,"iat":1709996400}
// Base64Url: eyJzdWIiOiJ1c2VyMTIzIiwiZXhwIjoxNzEwMDAwMDAwLCJpYXQiOjE3MDk5OTY0MDB9

// 3. 签名输入
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ1c2VyMTIzIiwiZXhwIjoxNzEwMDAwMDAwLCJpYXQiOjE3MDk5OTY0MDB9

// 4. 使用密钥 "mySecretKey123!" 进行 HMAC-SHA256
// 签名二进制 -> Base64Url
// 得到第三段: dGhpcyBpcyBhIHRlc3Qgc2lnbmF0dXJl

// 5. 完整 JWT
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ1c2VyMTIzIiwiZXhwIjoxNzEwMDAwMDAwLCJpYXQiOjE3MDk5OTY0MDB9.dGhpcyBpcyBhIHRlc3Qgc2lnbmF0dXJl
💡 最佳实践:生产环境中始终使用 exp(过期时间)声明,并配合 iat(签发时间)和 nbf(生效时间)进行时间窗口校验。签名算法优先选择 RS256 或 ES256,避免对称密钥在分布式系统中的分发风险。