JWT 结构 #

JWT 的三部分组成 #

JWT 由三个部分组成,用点号(.)分隔:

text
┌─────────────────────────────────────────────────────────────┐
│                    JWT 结构概览                              │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  xxxxx.yyyyy.zzzzz                                          │
│    │       │      │                                         │
│    │       │      └── Signature(签名)                     │
│    │       └───────── Payload(载荷)                       │
│    └───────────────── Header(头部)                        │
│                                                             │
│  完整示例:                                                  │
│  eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.                      │
│  eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0   │
│  IjoxNTE2MjM5MDIyfQ.                                        │
│  SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c                │
│                                                             │
└─────────────────────────────────────────────────────────────┘

第一部分:Header(头部) #

Header 的作用 #

Header 用于描述 JWT 的元数据,主要包括:

  • 令牌类型(typ)
  • 签名算法(alg)
  • 其他可选参数

Header 结构 #

json
{
  "alg": "HS256",
  "typ": "JWT"
}

Header 字段详解 #

text
┌─────────────────────────────────────────────────────────────┐
│                    Header 字段说明                           │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  字段名    │  必需  │  描述                                  │
│  ─────────┼───────┼─────────────────────────────────────── │
│  alg      │  是   │  签名算法                               │
│  typ      │  否   │  令牌类型,通常为 JWT                   │
│  cty      │  否   │  内容类型(嵌套 JWT 时使用)            │
│  kid      │  否   │  密钥标识符                             │
│  jku      │  否   │  JWKS URL                              │
│  x5u      │  否   │  X.509 URL                             │
│  x5c      │  否   │  X.509 证书链                          │
│  x5t      │  否   │  X.509 证书指纹                        │
│                                                             │
└─────────────────────────────────────────────────────────────┘

alg 字段(签名算法) #

text
┌─────────────────────────────────────────────────────────────┐
│                    常用签名算法                              │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  对称算法(HMAC):                                          │
│  ┌─────────┬───────────────────────────────────────────┐   │
│  │ HS256   │ HMAC SHA-256                              │   │
│  │ HS384   │ HMAC SHA-384                              │   │
│  │ HS512   │ HMAC SHA-512                              │   │
│  └─────────┴───────────────────────────────────────────┘   │
│                                                             │
│  非对称算法(RSA):                                         │
│  ┌─────────┬───────────────────────────────────────────┐   │
│  │ RS256   │ RSASSA-PKCS1-v1_5 SHA-256                 │   │
│  │ RS384   │ RSASSA-PKCS1-v1_5 SHA-384                 │   │
│  │ RS512   │ RSASSA-PKCS1-v1_5 SHA-512                 │   │
│  └─────────┴───────────────────────────────────────────┘   │
│                                                             │
│  非对称算法(ECDSA):                                       │
│  ┌─────────┬───────────────────────────────────────────┐   │
│  │ ES256   │ ECDSA P-256 SHA-256                       │   │
│  │ ES384   │ ECDSA P-384 SHA-384                       │   │
│  │ ES512   │ ECDSA P-521 SHA-512                       │   │
│  └─────────┴───────────────────────────────────────────┘   │
│                                                             │
│  其他:                                                     │
│  ┌─────────┬───────────────────────────────────────────┐   │
│  │ PS256   │ RSASSA-PSS SHA-256                        │   │
│  │ none    │ 无签名(不推荐)                           │   │
│  └─────────┴───────────────────────────────────────────┘   │
│                                                             │
└─────────────────────────────────────────────────────────────┘

kid 字段(密钥标识符) #

json
{
  "alg": "RS256",
  "typ": "JWT",
  "kid": "key-2024-01-01"
}

kid 用于标识用于验证签名的密钥,在密钥轮换场景中非常重要:

text
┌─────────────────────────────────────────────────────────────┐
│                    kid 的作用                                │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  1. 密钥轮换                                                │
│     - 多个密钥同时有效                                      │
│     - 通过 kid 选择正确的密钥验证                           │
│                                                             │
│  2. JWKS 集成                                               │
│     - 从 JWKS 端点获取公钥                                  │
│     - 根据 kid 匹配对应的公钥                               │
│                                                             │
│  示例流程:                                                  │
│                                                             │
│  JWT Header: { "kid": "key-001" }                          │
│       │                                                     │
│       ▼                                                     │
│  JWKS: { "keys": [                                         │
│    { "kid": "key-001", "n": "...", "e": "AQAB" },         │
│    { "kid": "key-002", "n": "...", "e": "AQAB" }          │
│  ]}                                                         │
│       │                                                     │
│       ▼                                                     │
│  使用 kid="key-001" 的公钥验证                              │
│                                                             │
└─────────────────────────────────────────────────────────────┘

Header 编码 #

text
┌─────────────────────────────────────────────────────────────┐
│                    Header 编码过程                           │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  1. 原始 JSON                                               │
│     {                                                       │
│       "alg": "HS256",                                       │
│       "typ": "JWT"                                          │
│     }                                                       │
│                                                             │
│  2. UTF-8 编码                                              │
│     {"alg":"HS256","typ":"JWT"}                             │
│                                                             │
│  3. Base64URL 编码                                          │
│     eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9                   │
│                                                             │
└─────────────────────────────────────────────────────────────┘

第二部分:Payload(载荷) #

Payload 的作用 #

Payload 包含实际传输的数据,称为"声明"(Claims)。声明是关于实体(通常是用户)和其他数据的声明。

Payload 结构 #

json
{
  "sub": "1234567890",
  "name": "John Doe",
  "iat": 1516239022
}

声明类型 #

text
┌─────────────────────────────────────────────────────────────┐
│                    JWT 声明类型                              │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  ┌─────────────────────────────────────────────────────┐   │
│  │  Registered Claims(注册声明)                       │   │
│  │  - 预定义的标准声明                                  │   │
│  │  - 推荐使用                                          │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                             │
│  ┌─────────────────────────────────────────────────────┐   │
│  │  Public Claims(公共声明)                           │   │
│  │  - 公开定义的声明                                    │   │
│  │  - 避免冲突                                          │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                             │
│  ┌─────────────────────────────────────────────────────┐   │
│  │  Private Claims(私有声明)                          │   │
│  │  - 自定义声明                                        │   │
│  │  - 仅在双方之间使用                                  │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                             │
└─────────────────────────────────────────────────────────────┘

Registered Claims(注册声明) #

text
┌─────────────────────────────────────────────────────────────┐
│                    标准注册声明                              │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  声明 │  名称           │  描述                              │
│  ────┼────────────────┼─────────────────────────────────── │
│  iss │  Issuer         │  签发者                            │
│  sub │  Subject        │  主题(用户标识)                  │
│  aud │  Audience       │  受众(接收方)                    │
│  exp │  Expiration     │  过期时间                          │
│  nbf │  Not Before     │  生效时间                          │
│  iat │  Issued At      │  签发时间                          │
│  jti │  JWT ID         │  唯一标识符                        │
│                                                             │
└─────────────────────────────────────────────────────────────┘

注册声明详解 #

iss(Issuer)- 签发者 #

json
{
  "iss": "https://auth.example.com"
}
text
作用:
- 标识 JWT 的签发者
- 验证时检查是否来自可信来源
- 通常是授权服务器的 URL

验证示例:
if (decoded.iss !== 'https://auth.example.com') {
  throw new Error('Invalid issuer');
}

sub(Subject)- 主题 #

json
{
  "sub": "user-123456"
}
text
作用:
- 标识 JWT 的主体
- 通常是用户唯一标识
- 在 iss 范围内必须唯一

使用场景:
- 用户 ID
- 服务标识
- 资源标识

aud(Audience)- 受众 #

json
{
  "aud": ["https://api.example.com", "client-app"]
}
text
作用:
- 标识 JWT 的接收者
- 防止 JWT 被错误的服务接受
- 可以是单个值或数组

验证示例:
const validAudiences = ['https://api.example.com'];
if (!validAudiences.includes(decoded.aud)) {
  throw new Error('Invalid audience');
}

exp(Expiration Time)- 过期时间 #

json
{
  "exp": 1516239022
}
text
作用:
- 定义 JWT 的过期时间
- 必须是 NumericDate(Unix 时间戳)
- 过期后 JWT 无效

验证示例:
const now = Math.floor(Date.now() / 1000);
if (decoded.exp < now) {
  throw new Error('Token expired');
}

nbf(Not Before)- 生效时间 #

json
{
  "nbf": 1516239000
}
text
作用:
- 定义 JWT 开始生效的时间
- 在此时间之前 JWT 无效
- 用于延迟生效场景

验证示例:
const now = Math.floor(Date.now() / 1000);
if (decoded.nbf > now) {
  throw new Error('Token not yet valid');
}

iat(Issued At)- 签发时间 #

json
{
  "iat": 1516239022
}
text
作用:
- 记录 JWT 的签发时间
- 可用于判断 Token 年龄
- 辅助安全验证

使用场景:
- 计算 Token 有效期
- 检测异常签发
- 审计追踪

jti(JWT ID)- 唯一标识符 #

json
{
  "jti": "550e8400-e29b-41d4-a716-446655440000"
}
text
作用:
- 为 JWT 提供唯一标识
- 用于防止重放攻击
- 可用于 Token 黑名单

使用场景:
- Token 撤销
- 重放防护
- 审计追踪

Public Claims(公共声明) #

text
┌─────────────────────────────────────────────────────────────┐
│                    公共声明                                  │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  公共声明是公开定义的,用于避免命名冲突。                     │
│  应该在 IANA JSON Web Token Registry 注册,                 │
│  或使用抗冲突命名方式(如 URI)。                            │
│                                                             │
│  IANA 注册示例:                                            │
│  - name: 用户名                                             │
│  - email: 邮箱                                              │
│  - picture: 头像 URL                                        │
│                                                             │
│  URI 命名示例:                                             │
│  {                                                          │
│    "https://example.com/claims/role": "admin",             │
│    "https://example.com/claims/tenant": "acme"             │
│  }                                                          │
│                                                             │
└─────────────────────────────────────────────────────────────┘

Private Claims(私有声明) #

json
{
  "sub": "user-123",
  "name": "John Doe",
  "email": "john@example.com",
  "role": "admin",
  "permissions": ["read", "write"],
  "tenant_id": "tenant-001"
}
text
┌─────────────────────────────────────────────────────────────┐
│                    私有声明                                  │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  私有声明是自定义的声明,仅在签发方和接收方之间使用。         │
│                                                             │
│  注意事项:                                                  │
│  ⚠️ 避免与注册声明冲突                                     │
│  ⚠️ 不要存储敏感信息                                       │
│  ⚠️ 控制声明数量(影响 Token 大小)                        │
│                                                             │
│  常见私有声明:                                              │
│  - role: 用户角色                                           │
│  - permissions: 权限列表                                    │
│  - tenant_id: 租户 ID                                       │
│  - department: 部门                                         │
│  - level: 用户等级                                          │
│                                                             │
└─────────────────────────────────────────────────────────────┘

完整 Payload 示例 #

json
{
  "iss": "https://auth.example.com",
  "sub": "user-123456",
  "aud": "https://api.example.com",
  "exp": 1516239022,
  "nbf": 1516239000,
  "iat": 1516238900,
  "jti": "550e8400-e29b-41d4-a716-446655440000",
  "name": "John Doe",
  "email": "john@example.com",
  "email_verified": true,
  "role": "admin",
  "permissions": ["read", "write", "delete"],
  "tenant_id": "tenant-001"
}

Payload 编码 #

text
┌─────────────────────────────────────────────────────────────┐
│                    Payload 编码过程                          │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  1. 原始 JSON                                               │
│     {                                                       │
│       "sub": "1234567890",                                  │
│       "name": "John Doe",                                   │
│       "iat": 1516239022                                     │
│     }                                                       │
│                                                             │
│  2. UTF-8 编码                                              │
│     {"sub":"1234567890","name":"John Doe","iat":1516239022} │
│                                                             │
│  3. Base64URL 编码                                          │
│     eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwi   │
│     aWF0IjoxNTE2MjM5MDIyfQ                                  │
│                                                             │
└─────────────────────────────────────────────────────────────┘

第三部分:Signature(签名) #

Signature 的作用 #

签名用于验证:

  1. 发送者身份(真实性)
  2. 消息未被篡改(完整性)

签名生成过程 #

text
┌─────────────────────────────────────────────────────────────┐
│                    签名生成流程                              │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  1. 准备签名内容                                            │
│     signatureContent = base64UrlEncode(header) + "." +     │
│                        base64UrlEncode(payload)            │
│                                                             │
│  2. 使用算法签名                                            │
│     HMACSHA256(                                            │
│       signatureContent,                                     │
│       secret                                                │
│     )                                                       │
│                                                             │
│  3. Base64URL 编码签名                                      │
│     signature = base64UrlEncode(hmacResult)                │
│                                                             │
│  4. 组合最终 JWT                                            │
│     jwt = header + "." + payload + "." + signature         │
│                                                             │
└─────────────────────────────────────────────────────────────┘

HMAC 签名示例(HS256) #

javascript
const header = {
  "alg": "HS256",
  "typ": "JWT"
};

const payload = {
  "sub": "1234567890",
  "name": "John Doe",
  "iat": 1516239022
};

const encodedHeader = base64UrlEncode(JSON.stringify(header));
const encodedPayload = base64UrlEncode(JSON.stringify(payload));

const signatureContent = encodedHeader + "." + encodedPayload;

const signature = HMACSHA256(signatureContent, 'your-256-bit-secret');

const jwt = encodedHeader + "." + encodedPayload + "." + base64UrlEncode(signature);

RSA 签名示例(RS256) #

javascript
const signatureContent = encodedHeader + "." + encodedPayload;

const signature = RSASign(
  signatureContent,
  privateKey,
  'sha256'
);

const jwt = encodedHeader + "." + encodedPayload + "." + base64UrlEncode(signature);

签名验证过程 #

text
┌─────────────────────────────────────────────────────────────┐
│                    签名验证流程                              │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  输入:JWT Token                                            │
│                                                             │
│  1. 分割 JWT                                                │
│     [header, payload, signature] = jwt.split('.')          │
│                                                             │
│  2. 解码 Header                                             │
│     headerObj = JSON.parse(base64UrlDecode(header))        │
│                                                             │
│  3. 获取签名密钥                                            │
│     - HMAC: 使用共享密钥                                    │
│     - RSA: 使用公钥                                         │
│                                                             │
│  4. 重新计算签名                                            │
│     expectedSignature = sign(header + "." + payload, key)  │
│                                                             │
│  5. 比对签名                                                │
│     if (signature === expectedSignature) {                 │
│       // 签名验证通过                                       │
│     } else {                                                │
│       // 签名验证失败                                       │
│     }                                                       │
│                                                             │
└─────────────────────────────────────────────────────────────┘

Base64URL 编码 #

为什么使用 Base64URL? #

text
┌─────────────────────────────────────────────────────────────┐
│                    Base64URL vs Base64                       │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  标准 Base64 问题:                                          │
│  - 包含 + / = 字符                                         │
│  - + 和 / 在 URL 中有特殊含义                               │
│  - = 作为填充字符                                           │
│                                                             │
│  Base64URL 改进:                                           │
│  - + 替换为 -                                              │
│  - / 替换为 _                                              │
│  - 去掉 = 填充                                             │
│                                                             │
│  对比示例:                                                  │
│  Base64:    a+b/c==                                         │
│  Base64URL: a-b_c                                           │
│                                                             │
└─────────────────────────────────────────────────────────────┘

Base64URL 编码实现 #

javascript
function base64UrlEncode(str) {
  return Buffer.from(str)
    .toString('base64')
    .replace(/\+/g, '-')
    .replace(/\//g, '_')
    .replace(/=/g, '');
}

function base64UrlDecode(str) {
  str = str.replace(/-/g, '+').replace(/_/g, '/');
  
  const pad = str.length % 4;
  if (pad) {
    str += '='.repeat(4 - pad);
  }
  
  return Buffer.from(str, 'base64').toString();
}

编码示例 #

text
┌─────────────────────────────────────────────────────────────┐
│                    编码示例                                  │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  原始字符串:                                                │
│  {"alg":"HS256","typ":"JWT"}                                │
│                                                             │
│  Base64 编码:                                              │
│  eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9                       │
│                                                             │
│  Base64URL 编码(相同,无特殊字符):                         │
│  eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9                       │
│                                                             │
│  ─────────────────────────────────────────────────────────  │
│                                                             │
│  原始字符串(包含特殊字符):                                 │
│  {"name":"张三?+"}                                          │
│                                                             │
│  Base64 编码:                                              │
│  eyJuYW1lIjoi5byg5LiJPyoi                                 │
│                                                             │
│  Base64URL 编码:                                           │
│  eyJuYW1lIjoi5byg5LiJPyoi                                  │
│                                                             │
└─────────────────────────────────────────────────────────────┘

完整 JWT 示例 #

生成 JWT #

javascript
const header = {
  "alg": "HS256",
  "typ": "JWT"
};

const payload = {
  "sub": "1234567890",
  "name": "John Doe",
  "iat": 1516239022
};

const encodedHeader = base64UrlEncode(JSON.stringify(header));
const encodedPayload = base64UrlEncode(JSON.stringify(payload));

const signature = HMACSHA256(
  encodedHeader + "." + encodedPayload,
  'your-256-bit-secret'
);

const jwt = encodedHeader + "." + encodedPayload + "." + base64UrlEncode(signature);

console.log(jwt);
// eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

解析 JWT #

javascript
function parseJwt(token) {
  const [header, payload, signature] = token.split('.');
  
  return {
    header: JSON.parse(base64UrlDecode(header)),
    payload: JSON.parse(base64UrlDecode(payload)),
    signature: signature
  };
}

const parsed = parseJwt(jwt);

console.log(parsed.header);
// { alg: "HS256", typ: "JWT" }

console.log(parsed.payload);
// { sub: "1234567890", name: "John Doe", iat: 1516239022 }

JWT 大小计算 #

text
┌─────────────────────────────────────────────────────────────┐
│                    JWT 大小分析                              │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  Header(约 35-50 字节):                                   │
│  {"alg":"HS256","typ":"JWT"}                                │
│                                                             │
│  Payload(取决于内容):                                     │
│  最小:约 50 字节(仅标准声明)                              │
│  典型:约 150-300 字节                                      │
│  较大:500+ 字节(包含大量自定义声明)                       │
│                                                             │
│  Signature:                                                │
│  HS256:32 字节 → Base64URL 后 43 字符                      │
│  RS256:256 字节 → Base64URL 后 342 字符                    │
│                                                             │
│  总大小示例:                                                │
│  - 简单 JWT(HS256):约 200 字节                           │
│  - 典型 JWT(HS256):约 300-500 字节                       │
│  - 典型 JWT(RS256):约 600-800 字节                       │
│                                                             │
│  注意:                                                     │
│  ⚠️ 每次请求都要传输                                       │
│  ⚠️ 控制声明数量                                           │
│  ⚠️ 考虑网络开销                                           │
│                                                             │
└─────────────────────────────────────────────────────────────┘

下一步 #

现在你已经了解了 JWT 的结构,接下来学习 JWT 签名算法,深入了解不同签名算法的特点和使用场景!

最后更新:2026-03-28