4. JWT 生成与解析:使用 Python PyJWT 库
在分布式鉴权系统中,JWT 的生成与解析是核心操作。Python 生态中最成熟的 JWT 库是 PyJWT,它提供了简洁的 API 用于创建、解码和验证 Token。本节将带你从零开始,掌握使用 PyJWT 进行 Token 生命周期管理的完整流程。
4.1 环境准备与库安装
首先,确保你的 Python 环境(3.8+)已安装 PyJWT 库。推荐同时安装 cryptography 包以支持 RSA/ECDSA 非对称算法。
# 安装 PyJWT(支持 HMAC 对称算法)
pip install pyjwt
# 安装 cryptography(支持 RS256/ES256 等非对称算法)
pip install cryptography
版本提示: 本文基于 PyJWT 2.8+ 版本。旧版本(1.x)的
decode() 方法默认不验证签名,存在安全风险,请务必升级。
4.2 生成 Token(编码)
生成 JWT 的核心是调用 jwt.encode() 方法。你需要准备三要素:Payload(载荷)、Secret Key(密钥)、Algorithm(算法)。
4.2.1 使用 HS256 对称算法生成 Token
import jwt
import datetime
# 1. 定义密钥(生产环境应从环境变量或密钥管理服务获取)
SECRET_KEY = "your-256-bit-secret-key-here"
# 2. 构建 Payload(标准声明 + 私有声明)
payload = {
# 标准注册声明
"iss": "auth-service", # 签发者
"sub": "user_12345", # 主题(用户ID)
"aud": "api-gateway", # 接收方
"exp": datetime.datetime.utcnow() + datetime.timedelta(hours=2), # 过期时间
"iat": datetime.datetime.utcnow(), # 签发时间
"nbf": datetime.datetime.utcnow(), # 生效时间
# 私有声明(自定义数据)
"username": "alice",
"role": "admin",
"permissions": ["read:users", "write:users"]
}
# 3. 生成 Token
token = jwt.encode(
payload=payload,
key=SECRET_KEY,
algorithm="HS256"
)
print(f"生成的 JWT:\n{token}")
4.2.2 使用 RS256 非对称算法生成 Token
非对称算法使用私钥签名、公钥验证,适合微服务间跨信任域的场景。
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography.hazmat.backends import default_backend
import jwt, datetime
# 生成 RSA 密钥对(生产环境应使用预先生成的密钥文件)
private_key = rsa.generate_private_key(
public_exponent=65537,
key_size=2048,
backend=default_backend()
)
# 序列化私钥(PEM 格式)
private_pem = private_key.private_bytes(
encoding=serialization.Encoding.PEM,
format=serialization.PrivateFormat.PKCS8,
encryption_algorithm=serialization.NoEncryption()
)
# 提取公钥
public_key = private_key.public_key()
public_pem = public_key.public_bytes(
encoding=serialization.Encoding.PEM,
format=serialization.PublicFormat.SubjectPublicKeyInfo
)
# 使用私钥签名生成 Token
payload = {
"sub": "user_67890",
"role": "viewer",
"exp": datetime.datetime.utcnow() + datetime.timedelta(minutes=30)
}
token_rs256 = jwt.encode(
payload=payload,
key=private_pem,
algorithm="RS256"
)
print(f"RS256 Token:\n{token_rs256}")
4.3 解析 Token(解码与验证)
解析 Token 包含两个步骤:解码(Decode) 和 验证(Verify)。PyJWT 的 jwt.decode() 方法会同时完成这两步。
4.3.1 解析 HS256 Token
try:
# 解码并验证签名
decoded_payload = jwt.decode(
jwt=token, # 待解析的 Token 字符串
key=SECRET_KEY, # 对称密钥(与生成时相同)
algorithms=["HS256"], # 允许的算法列表
audience="api-gateway", # 验证 aud 声明
issuer="auth-service", # 验证 iss 声明
options={
"verify_exp": True, # 验证过期时间
"verify_iat": True, # 验证签发时间
"require": ["exp", "sub", "aud"] # 强制要求这些声明存在
}
)
print(f"解析成功,Payload: {decoded_payload}")
print(f"用户: {decoded_payload['username']}, 角色: {decoded_payload['role']}")
except jwt.ExpiredSignatureError:
print("错误:Token 已过期")
except jwt.InvalidAudienceError:
print("错误:接收方不匹配")
except jwt.InvalidIssuerError:
print("错误:签发者不匹配")
except jwt.InvalidTokenError as e:
print(f"错误:无效 Token - {e}")
4.3.2 解析 RS256 Token
try:
# 使用公钥验证签名
decoded_rs256 = jwt.decode(
jwt=token_rs256,
key=public_pem, # 公钥(非对称验证)
algorithms=["RS256"],
options={"verify_exp": True}
)
print(f"RS256 解析成功: {decoded_rs256}")
except jwt.PyJWTError as e:
print(f"验证失败: {e}")
安全警告: 永远不要使用
jwt.decode(token, verify=False) 或旧版 jwt.decode(token, key=None)。这会跳过签名验证,攻击者可以伪造任意 Token 绕过鉴权。
4.4 签名验证流程详解
下图展示了 PyJWT 在 decode() 方法内部执行的完整验证流水线:
4.5 常用算法对比与选择
| 算法 | 类型 | 密钥管理 | 性能 | 适用场景 |
|---|---|---|---|---|
| HS256 | 对称(HMAC+SHA256) | 单一密钥,需安全共享 | 高(纯哈希运算) | 单体应用、内部服务间信任 |
| RS256 | 非对称(RSA+SHA256) | 私钥签名,公钥验证 | 中(RSA 运算较慢) | 微服务、第三方集成、跨信任域 |
| ES256 | 非对称(ECDSA+P-256) | 私钥签名,公钥验证 | 高(椭圆曲线更高效) | 移动端、IoT、高性能要求场景 |
选型建议: 对于大多数分布式系统,推荐使用 RS256 或 ES256。非对称算法允许认证服务(持有私钥)独立于资源服务(持有公钥),公钥可以安全地通过配置中心或 JWKS 端点分发,无需共享敏感密钥。
4.6 常见错误与调试
- ExpiredSignatureError:Token 已过期。检查系统时间是否同步(NTP),或延长
exp时间。 - InvalidSignatureError:签名不匹配。检查密钥是否一致,或 Token 是否被篡改。
- DecodeError:Token 格式错误。确认 Token 是否为三段式(header.payload.signature),且使用 Base64URL 编码。
- InvalidAlgorithmError:算法不匹配。检查
algorithms参数是否包含 Token header 中的算法。 - MissingRequiredClaimError:缺少必要声明。检查
require参数中指定的声明是否在 Payload 中存在。
4.7 完整实战:Token 生命周期管理
以下代码演示了从生成到验证的完整闭环,包含错误处理和最佳实践:
import jwt
import datetime
from typing import Dict, Optional
class JWTManager:
"""JWT 管理器(HS256 示例)"""
def __init__(self, secret_key: str, algorithm: str = "HS256"):
self.secret_key = secret_key
self.algorithm = algorithm
def create_token(self, user_id: str, role: str,
expire_hours: int = 2) -> str:
"""生成访问 Token"""
payload = {
"sub": user_id,
"role": role,
"iat": datetime.datetime.utcnow(),
"exp": datetime.datetime.utcnow() + datetime.timedelta(hours=expire_hours)
}
return jwt.encode(payload, self.secret_key, algorithm=self.algorithm)
def verify_token(self, token: str) -> Optional[Dict]:
"""验证并解析 Token,成功返回 Payload,失败返回 None"""
try:
payload = jwt.decode(
token,
self.secret_key,
algorithms=[self.algorithm],
options={"require": ["sub", "exp"]}
)
return payload
except jwt.ExpiredSignatureError:
print("Token 已过期,请重新登录")
except jwt.InvalidTokenError as e:
print(f"Token 无效: {e}")
return None
# 使用示例
manager = JWTManager("my-secret-key-12345")
token = manager.create_token("user_001", "admin")
print(f"生成 Token: {token[:50]}...")
payload = manager.verify_token(token)
if payload:
print(f"验证通过: 用户 {payload['sub']}, 角色 {payload['role']}")
4.8 安全最佳实践
- 密钥强度:HS256 密钥至少 256 位(32 字节),使用
secrets.token_hex(32)生成。 - 过期时间:访问 Token 建议 15 分钟 - 2 小时;刷新 Token 可设置 7-30 天。
- 算法白名单:
decode()时始终指定algorithms参数,防止算法混淆攻击。 - 声明验证:验证
aud(接收方)和iss(签发者)防止 Token 被重放到其他服务。 - 密钥轮换:定期更换密钥,旧密钥保留一段缓冲期用于验证已签发的 Token。
生产环境提示: 不要将密钥硬编码在代码中。使用环境变量、Vault、AWS Secrets Manager 或 Kubernetes Secrets 管理密钥。对于非对称算法,使用 JWKS(JSON Web Key Set)端点动态分发公钥。