JWT 高级主题 #
JWKS(JSON Web Key Set) #
什么是 JWKS? #
JWKS 是一组公钥的 JSON 表示,用于验证 JWT 签名。它允许客户端动态获取验证密钥。
text
┌─────────────────────────────────────────────────────────────┐
│ JWKS 工作原理 │
├─────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────┐ ┌─────────┐ │
│ │ 客户端 │ │ 授权服务 │ │
│ └────┬────┘ └────┬────┘ │
│ │ │ │
│ │ 1. 获取 JWKS │ │
│ │─────────────────────────>│ │
│ │ │ │
│ │ 2. 返回公钥集合 │ │
│ │<─────────────────────────│ │
│ │ │ │
│ │ 3. 接收 JWT │ │
│ │ │ │
│ │ 4. 从 Header 获取 kid │ │
│ │ │ │
│ │ 5. 匹配 JWKS 中的公钥 │ │
│ │ │ │
│ │ 6. 验证签名 │ │
│ │
└─────────────────────────────────────────────────────────────┘
JWKS 格式 #
json
{
"keys": [
{
"kty": "RSA",
"kid": "key-2024-01",
"use": "sig",
"alg": "RS256",
"n": "0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx4cbbfAAtVT86zwu1RK7aPFFxuhDR1L6tSoc_BJECPebWKRXjBZCiFV4n3oknjhMstn64tZ_2W-5JsGY4Hc5n9yBXArwl93lqt7_RN5w6Cf0h4QyQ5v-65YGjQR0_FDW2QvzqY368QQMicAtaSqzs8KJZgnYb9c7d0zgdAZHzu6qMQvRL5hajrn1n91CbOpbISD08qNLyrdkt-bFTWhAI4vMQFh6WeZu0fM4lFd2NcRwr3XPksINHaQ-G_xBniIqbw0Ls1jF44-csFCur-kEgU8awapJzKnqDKgw",
"e": "AQAB"
},
{
"kty": "EC",
"kid": "key-2024-02",
"use": "sig",
"alg": "ES256",
"crv": "P-256",
"x": "MKBCTNIcKUSDii11ySs3526iDZ8AiTo7Tu6KPAqv7D4",
"y": "4Etl6SRW2YiLUrN5vfvVHuhp7x8PxltmWWlbbM4IFyM"
}
]
}
JWKS 字段说明 #
text
┌─────────────────────────────────────────────────────────────┐
│ JWKS 字段说明 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 通用字段: │
│ ───────────────────────────────────────────────────────── │
│ kty 密钥类型(RSA, EC, oct) │
│ kid 密钥标识符 │
│ use 用途(sig 签名, enc 加密) │
│ alg 算法 │
│ │
│ RSA 密钥: │
│ ───────────────────────────────────────────────────────── │
│ n 模数 │
│ e 公开指数 │
│ │
│ EC 密钥: │
│ ───────────────────────────────────────────────────────── │
│ crv 曲线(P-256, P-384, P-521) │
│ x X 坐标 │
│ y Y 坐标 │
│ │
│ 对称密钥: │
│ ───────────────────────────────────────────────────────── │
│ k 密钥值(Base64URL 编码) │
│ │
└─────────────────────────────────────────────────────────────┘
使用 JWKS 验证 JWT #
javascript
const jwt = require('jsonwebtoken');
const jwksClient = require('jwks-rsa');
const client = jwksClient({
jwksUri: 'https://auth.example.com/.well-known/jwks.json',
cache: true,
cacheMaxAge: 86400000,
rateLimit: true,
jwksRequestsPerMinute: 10
});
function getKey(header, callback) {
client.getSigningKey(header.kid, (err, key) => {
if (err) {
callback(err, null);
return;
}
const signingKey = key.publicKey || key.rsaPublicKey;
callback(null, signingKey);
});
}
function verifyToken(token) {
return new Promise((resolve, reject) => {
jwt.verify(token, getKey, {
algorithms: ['RS256'],
issuer: 'https://auth.example.com',
audience: 'https://api.example.com'
}, (err, decoded) => {
if (err) {
reject(err);
} else {
resolve(decoded);
}
});
});
}
Python 使用 JWKS #
python
import jwt
import requests
from jwt import PyJWKClient
jwks_url = 'https://auth.example.com/.well-known/jwks.json'
jwks_client = PyJWKClient(jwks_url)
def verify_token(token):
signing_key = jwks_client.get_signing_key_from_jwt(token)
return jwt.decode(
token,
signing_key.key,
algorithms=['RS256'],
issuer='https://auth.example.com',
audience='https://api.example.com'
)
密钥轮换最佳实践 #
text
┌─────────────────────────────────────────────────────────────┐
│ 密钥轮换流程 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 1. 生成新密钥 │
│ - 创建新的公私钥对 │
│ - 分配新的 kid │
│ │
│ 2. 添加到 JWKS │
│ - 新密钥添加到 keys 数组 │
│ - JWKS 现在包含多个密钥 │
│ │
│ 3. 使用新密钥签名 │
│ - 新 Token 使用新密钥 │
│ - Header 包含新 kid │
│ │
│ 4. 过渡期 │
│ - 旧 Token 仍可用旧公钥验证 │
│ - 新 Token 用新公钥验证 │
│ │
│ 5. 移除旧密钥 │
│ - 等待所有旧 Token 过期 │
│ - 从 JWKS 移除旧密钥 │
│ │
│ 建议轮换周期: │
│ - 高安全:30 天 │
│ - 中安全:90 天 │
│ - 低安全:180 天 │
│ │
└─────────────────────────────────────────────────────────────┘
JWE(JSON Web Encryption) #
什么是 JWE? #
JWE 用于加密 JWT 的内容,确保只有授权方可以读取 Payload。
text
┌─────────────────────────────────────────────────────────────┐
│ JWE 结构 │
├─────────────────────────────────────────────────────────────┤
│ │
│ JWE 由五部分组成: │
│ │
│ header.encryptedKey.iv.ciphertext.tag │
│ │ │ │ │ │ │
│ │ │ │ │ └── 认证标签 │
│ │ │ │ └── 加密后的内容 │
│ │ │ └── 初始化向量 │
│ │ └── 加密的密钥 │
│ └── JOSE Header │
│ │
│ 示例: │
│ eyJhbGciOiJSU0EtT0FFUCIsImVuYyI6IkEyNTZHQ00ifQ. │
│ OKOawDo13gRp2ojaHV7LFpZcgVbTqDVWx3HbL9N3... │
│ 48V1_ALb6US04U3b. │
│ 5eym8TW_c8SuK0ltJ3rpYIzOeDQz7TALvtu6UG9oMo4vpzs9tX_EFShS8 │
│ iB7j6jiSdi6IrUdNCmZzmL0. │
│ XFBoMYUZodetZdvTiFvSkQ │
│ │
└─────────────────────────────────────────────────────────────┘
JWE 加密示例 #
javascript
const jose = require('node-jose');
async function encryptJWT(payload, publicKey) {
const key = await jose.JWK.asKey(publicKey, 'pem');
const token = await jose.JWE.createEncrypt({
format: 'compact',
fields: {
alg: 'RSA-OAEP',
enc: 'A256GCM'
}
})
.update(JSON.stringify(payload))
.final(key);
return token;
}
async function decryptJWT(token, privateKey) {
const key = await jose.JWK.asKey(privateKey, 'pem');
const result = await jose.JWE.createDecrypt(key)
.decrypt(token);
return JSON.parse(result.payload.toString());
}
JWE vs JWS #
text
┌─────────────────────────────────────────────────────────────┐
│ JWE vs JWS 对比 │
├─────────────────────────────────────────────────────────────┤
│ │
│ JWS(JSON Web Signature): │
│ ───────────────────────────────────────────────────────── │
│ - 签名验证 │
│ - 保证完整性 │
│ - 保证真实性 │
│ - Payload 可见 │
│ - 常用于授权 Token │
│ │
│ JWE(JSON Web Encryption): │
│ ───────────────────────────────────────────────────────── │
│ - 加密内容 │
│ - 保证机密性 │
│ - Payload 不可见 │
│ - 常用于敏感数据传输 │
│ │
│ 选择建议: │
│ - 一般授权:使用 JWS │
│ - 敏感数据:使用 JWE 或 JWS + JWE │
│ - 高安全场景:嵌套 JWT(JWE 包装 JWS) │
│ │
└─────────────────────────────────────────────────────────────┘
嵌套 JWT #
什么是嵌套 JWT? #
嵌套 JWT 是将一个签名的 JWT(JWS)加密在另一个 JWT(JWE)中。
text
┌─────────────────────────────────────────────────────────────┐
│ 嵌套 JWT 结构 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 外层 JWE(加密): │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Header: { "alg": "RSA-OAEP", "enc": "A256GCM", │ │
│ │ "cty": "JWT" } │ │
│ │ │ │
│ │ Payload: 内层 JWS │ │
│ └─────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ 内层 JWS(签名): │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Header: { "alg": "RS256", "typ": "JWT" } │ │
│ │ Payload: { "sub": "user-123", ... } │ │
│ │ Signature: ... │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ 优势: │
│ ✅ 同时提供签名和加密 │
│ ✅ 保证完整性、真实性、机密性 │
│ ✅ 适用于高安全场景 │
│ │
└─────────────────────────────────────────────────────────────┘
创建嵌套 JWT #
javascript
const jose = require('node-jose');
async function createNestedJWT(payload, signingKey, encryptionKey) {
const signKey = await jose.JWK.asKey(signingKey, 'pem');
const encKey = await jose.JWK.asKey(encryptionKey, 'pem');
const jws = await jose.JWS.createSign({
format: 'compact',
fields: {
alg: 'RS256',
typ: 'JWT'
}
})
.update(JSON.stringify(payload))
.final(signKey);
const jwe = await jose.JWE.createEncrypt({
format: 'compact',
fields: {
alg: 'RSA-OAEP',
enc: 'A256GCM',
cty: 'JWT'
}
})
.update(jws)
.final(encKey);
return jwe;
}
Token 绑定 #
什么是 Token 绑定? #
Token 绑定将 JWT 与特定客户端绑定,防止 Token 被盗用后在其他设备使用。
text
┌─────────────────────────────────────────────────────────────┐
│ Token 绑定方式 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 1. 证书绑定(Certificate Binding) │
│ ───────────────────────────────────────────────────────── │
│ - 将 Token 与客户端证书绑定 │
│ - 使用证书公钥的哈希 │
│ - 适用于企业环境 │
│ │
│ 2. DPoP(Demonstrating Proof of Possession) │
│ ───────────────────────────────────────────────────────── │
│ - 客户端生成公私钥对 │
│ - 每个请求用私钥签名 │
│ - Token 包含公钥哈希 │
│ │
│ 3. MTLS(Mutual TLS) │
│ ───────────────────────────────────────────────────────── │
│ - 双向 TLS 认证 │
│ - Token 与客户端证书绑定 │
│ - 高安全性 │
│ │
└─────────────────────────────────────────────────────────────┘
DPoP 实现 #
javascript
const jose = require('node-jose');
const crypto = require('crypto');
async function generateDPoPKey() {
const key = await jose.JWK.createKey('EC', 'P-256', {
use: 'sig',
alg: 'ES256'
});
return key;
}
async function createDPoPProof(method, url, accessToken, key) {
const thumbprint = await key.thumbprint('SHA-256');
const header = {
typ: 'dpop+jwt',
alg: 'ES256',
jwk: key.toJSON()
};
const payload = {
htu: url,
htm: method,
iat: Math.floor(Date.now() / 1000),
jti: crypto.randomBytes(16).toString('hex'),
ath: crypto.createHash('sha256').update(accessToken).digest('base64url')
};
const proof = await jose.JWS.createSign({
format: 'compact',
fields: header
})
.update(JSON.stringify(payload))
.final(key);
return proof;
}
性能优化 #
缓存策略 #
javascript
const NodeCache = require('node-cache');
const jwksClient = require('jwks-rsa');
const keyCache = new NodeCache({
stdTTL: 3600,
checkperiod: 600
});
const client = jwksClient({
jwksUri: 'https://auth.example.com/.well-known/jwks.json',
cache: true,
cacheMaxAge: 86400000
});
async function getSigningKey(kid) {
const cacheKey = `key:${kid}`;
let key = keyCache.get(cacheKey);
if (!key) {
const signingKey = await client.getSigningKey(kid);
key = signingKey.publicKey || signingKey.rsaPublicKey;
keyCache.set(cacheKey, key);
}
return key;
}
批量验证 #
javascript
async function verifyMultipleTokens(tokens) {
const results = await Promise.allSettled(
tokens.map(token => verifyToken(token))
);
return results.map((result, index) => ({
token: tokens[index],
valid: result.status === 'fulfilled',
payload: result.status === 'fulfilled' ? result.value : null,
error: result.status === 'rejected' ? result.reason.message : null
}));
}
Token 大小优化 #
text
┌─────────────────────────────────────────────────────────────┐
│ Token 大小优化建议 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 1. 减少声明数量 │
│ - 只包含必要的声明 │
│ - 使用简短的声明名称 │
│ - 避免重复信息 │
│ │
│ 2. 使用引用代替完整数据 │
│ - 存储用户 ID 而非完整用户对象 │
│ - 服务端按需获取详细信息 │
│ │
│ 3. 选择合适的算法 │
│ - HMAC 签名最短(32-64 字节) │
│ - ECDSA 签名中等(64-132 字节) │
│ - RSA 签名最长(256 字节) │
│ │
│ 4. 压缩(可选) │
│ - 使用 DEFLATE 压缩 Payload │
│ - 适用于大量声明 │
│ │
│ 示例对比: │
│ 优化前:~800 字节 │
│ 优化后:~300 字节 │
│ │
└─────────────────────────────────────────────────────────────┘
JWT 调试工具 #
在线调试 #
text
┌─────────────────────────────────────────────────────────────┐
│ JWT 调试工具 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 在线工具: │
│ - jwt.io 官方调试器 │
│ - jwt.ms Microsoft 调试器 │
│ - jwt-debugger.com 社区调试器 │
│ │
│ 命令行工具: │
│ - jwt-cli JWT 命令行工具 │
│ - jq JSON 处理 │
│ │
│ 浏览器扩展: │
│ - JWT Inspector Chrome 扩展 │
│ - JWT Debugger Firefox 扩展 │
│ │
│ 注意: │
│ ⚠️ 不要将生产密钥输入在线工具 │
│ ⚠️ 不要将敏感 Token 分享给第三方 │
│ │
└─────────────────────────────────────────────────────────────┘
命令行调试 #
bash
jwt decode eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
echo "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." | cut -d'.' -f2 | base64 -d | jq
Node.js 调试脚本 #
javascript
const jwt = require('jsonwebtoken');
function debugToken(token) {
const decoded = jwt.decode(token, { complete: true });
console.log('=== JWT Debug Info ===');
console.log('\nHeader:');
console.log(JSON.stringify(decoded.header, null, 2));
console.log('\nPayload:');
console.log(JSON.stringify(decoded.payload, null, 2));
console.log('\nSignature:');
console.log(decoded.signature);
const now = Math.floor(Date.now() / 1000);
console.log('\nValidation:');
console.log(`- Issued at: ${new Date(decoded.payload.iat * 1000).toISOString()}`);
console.log(`- Expires at: ${new Date(decoded.payload.exp * 1000).toISOString()}`);
console.log(`- Is expired: ${decoded.payload.exp < now}`);
if (decoded.payload.nbf) {
console.log(`- Not before: ${new Date(decoded.payload.nbf * 1000).toISOString()}`);
console.log(`- Is valid: ${decoded.payload.nbf <= now}`);
}
}
debugToken(process.argv[2]);
JWT 规范扩展 #
常见扩展 #
text
┌─────────────────────────────────────────────────────────────┐
│ JWT 规范扩展 │
├─────────────────────────────────────────────────────────────┤
│ │
│ RFC 8725 - JWT 最佳实践 │
│ ───────────────────────────────────────────────────────── │
│ - 算法选择建议 │
│ - 安全验证要求 │
│ - 常见陷阱避免 │
│ │
│ RFC 9068 - JWT 用于 OAuth 2.0 │
│ ───────────────────────────────────────────────────────── │
│ - JWT 作为访问令牌的规范 │
│ - 标准声明定义 │
│ - 验证要求 │
│ │
│ RFC 7800 - Proof of Possession │
│ ───────────────────────────────────────────────────────── │
│ - cnf 声明 │
│ - 密钥绑定 │
│ │
│ OpenID Connect │
│ ───────────────────────────────────────────────────────── │
│ - ID Token 规范 │
│ - 标准声明 │
│ - 用户信息 │
│ │
└─────────────────────────────────────────────────────────────┘
下一步 #
现在你已经掌握了 JWT 高级主题,接下来学习 JWT 实战实现,通过完整的项目案例巩固所学知识!
最后更新:2026-03-28