3. Python JWT 库介绍:PyJWT 安装、HS256 算法签名与验签示例
在 Python 生态中,PyJWT 是最广泛使用的 JWT 库之一。它轻量、安全,支持多种签名算法(HS256、RS256、ES256 等),并且与 Flask、Django、FastAPI 等框架无缝集成。本章将带你完成 PyJWT 的安装,并通过 HS256 对称算法实现完整的签名与验签流程。
3.1 PyJWT 安装与环境准备
PyJWT 可通过 pip 直接安装。建议在虚拟环境中进行,避免与系统级包冲突。
# 创建并激活虚拟环境(可选但推荐)
python -m venv venv
source venv/bin/activate # Linux/macOS
# venv\Scripts\activate # Windows
# 安装 PyJWT
pip install pyjwt
# 验证安装
python -c "import jwt; print(jwt.__version__)"
版本提示: 本文基于 PyJWT 2.8+。如果你使用旧版本(如 1.x),部分 API 名称和默认行为可能不同。建议升级到最新版。
3.2 HS256 算法核心概念
HS256(HMAC with SHA-256)是一种对称签名算法:签发和验证使用同一个密钥(secret)。其核心流程如下:
- 签名(Sign):使用密钥对 Header + Payload 进行 HMAC-SHA256 运算,生成签名,附加到 JWT 末尾。
- 验签(Verify):使用相同密钥对接收到的 JWT 重新计算签名,比对是否一致。若不一致,则拒绝该 Token。
安全警告: 对称密钥必须严格保密,不可硬编码在代码仓库中。生产环境应通过环境变量、密钥管理服务(如 AWS KMS、HashiCorp Vault)或配置中心注入。
3.3 使用 HS256 签名 JWT
签名过程需要三个要素:Payload(载荷)、密钥、算法标识。PyJWT 的 jwt.encode() 函数自动完成 Header 构建、Base64 编码和签名计算。
import jwt
import datetime
# 1. 定义密钥(生产环境请从安全配置读取)
SECRET_KEY = "my-secret-key-2024"
# 2. 构建 Payload(标准声明 + 自定义声明)
payload = {
"sub": "user_12345", # 主题(用户ID)
"name": "张三", # 自定义声明
"role": "admin", # 自定义声明
"iat": datetime.datetime.utcnow(), # 签发时间
"exp": datetime.datetime.utcnow() + datetime.timedelta(hours=1) # 过期时间
}
# 3. 签名:指定算法为 HS256
token = jwt.encode(payload, SECRET_KEY, algorithm="HS256")
print("生成的 JWT:")
print(token)
输出示例:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ1c2VyXzEyMzQ1IiwibmFtZSI6IuW8oOS4iSIsInJvbGUiOiJhZG1pbiIsImlhdCI6MTcwMDAwMDAwMCwiZXhwIjoxNzAwMDAzNjAwfQ.abc123def456...
3.4 使用 HS256 验证 JWT
验签使用 jwt.decode() 函数。PyJWT 会自动提取 Header 中的算法信息,使用提供的密钥重新计算签名并比对。若签名无效、Token 过期或格式错误,会抛出相应异常。
# 4. 验证 JWT(使用相同密钥)
try:
decoded_payload = jwt.decode(
token,
SECRET_KEY,
algorithms=["HS256"] # 明确指定允许的算法
)
print("验签成功!Payload:")
print(decoded_payload)
except jwt.ExpiredSignatureError:
print("错误:Token 已过期")
except jwt.InvalidTokenError as e:
print(f"错误:无效 Token - {e}")
最佳实践:
algorithms 参数应明确指定为 ["HS256"],而不是留空或使用 None。这可以防止算法混淆攻击(Algorithm Confusion Attack),即攻击者将算法改为 none 或非对称算法来绕过验证。
3.5 常见异常与处理策略
| 异常类 | 触发条件 | 处理建议 |
|---|---|---|
ExpiredSignatureError |
Token 的 exp 时间已过 |
提示用户重新登录,或使用 Refresh Token 刷新 |
InvalidSignatureError |
签名不匹配(密钥错误或 Token 被篡改) | 拒绝请求,记录安全日志 |
DecodeError |
JWT 格式错误(段数不对、Base64 解码失败) | 返回 400 Bad Request,提示客户端检查 Token 格式 |
InvalidAlgorithmError |
Token 的算法不在 algorithms 白名单中 |
拒绝请求,防止算法混淆攻击 |
3.6 完整示例:签名与验签一体化
以下代码将签名和验签封装为两个函数,便于在项目中复用:
import jwt
import datetime
from typing import Dict, Optional
SECRET_KEY = "my-secret-key-2024"
def sign_jwt(payload: Dict) -> str:
"""使用 HS256 签名 JWT"""
# 自动添加 iat 和 exp(可选)
payload.setdefault("iat", datetime.datetime.utcnow())
payload.setdefault("exp", datetime.datetime.utcnow() + datetime.timedelta(hours=1))
return jwt.encode(payload, SECRET_KEY, algorithm="HS256")
def verify_jwt(token: str) -> Optional[Dict]:
"""验证 JWT,成功返回 Payload,失败返回 None"""
try:
return jwt.decode(token, SECRET_KEY, algorithms=["HS256"])
except jwt.PyJWTError:
return None
# 使用示例
if __name__ == "__main__":
user_payload = {"sub": "user_001", "name": "李四", "role": "editor"}
token = sign_jwt(user_payload)
print(f"Token: {token}")
result = verify_jwt(token)
if result:
print(f"验证通过,用户: {result['name']}, 角色: {result['role']}")
else:
print("验证失败")
3.7 HS256 与 RS256 对比
HS256 适合内部服务间通信,RS256(RSA 非对称算法)适合跨信任域的场景。下表列出关键差异:
| 对比维度 | HS256(对称) | RS256(非对称) |
|---|---|---|
| 密钥数量 | 1 个共享密钥 | 私钥 + 公钥(2 个) |
| 签名速度 | 快 | 慢(约慢 10-100 倍) |
| 验签速度 | 快 | 快(公钥验签) |
| 密钥分发 | 需安全通道共享密钥 | 公钥可公开,私钥仅签发方持有 |
| 适用场景 | 微服务内部、单点登录(SSO) | 第三方 API、跨组织认证 |
3.8 本章小结
- PyJWT 安装简单,
pip install pyjwt即可使用。 - HS256 是对称算法,签名与验签使用同一密钥,适合内部服务。
jwt.encode()生成 Token,jwt.decode()验证 Token,需明确指定algorithms=["HS256"]。- 务必处理
ExpiredSignatureError、InvalidSignatureError等异常,避免应用崩溃。 - 对称密钥必须安全存储,非对称算法(RS256)适用于跨信任域场景。