4. JWT 生成实战:使用 PyJWT 库生成 HS256 签名的 JWT Token
本章将进入实战环节,使用 Python 生态中最流行的 PyJWT 库,生成一个采用 HS256(HMAC with SHA-256)签名算法的 JWT Token。通过动手编码,你将掌握 JWT 的生成流程、载荷结构、签名计算以及常见配置项。
4.1 环境准备与库安装
在开始编码之前,请确保你的 Python 环境(3.8+)中已安装 PyJWT 库。推荐使用 pip 进行安装:
pip install pyjwt
安装完成后,可通过以下命令验证版本:
python -c "import jwt; print(jwt.__version__)"
版本提示: 本课程基于 PyJWT 2.8+ 版本。若使用旧版本(如 1.x),部分 API 行为可能存在差异,建议升级至最新稳定版。
4.2 HS256 签名算法核心参数
HS256 属于对称签名算法,即 签名与验签使用同一个密钥。下表列出了生成 HS256 JWT 时必须关注的核心参数:
| 参数 | 说明 | 示例值 |
|---|---|---|
| 密钥(secret) | 对称密钥,至少 32 字节(256 位)以保证安全性 | "my-256-bit-secret-key-here!@#$" |
| 算法(algorithm) | 指定签名算法,HS256 对应 "HS256" |
"HS256" |
| 载荷(payload) | 包含声明(claims)的字典,至少包含 sub、exp 等标准声明 |
{"sub": "user123", "exp": 1718000000} |
| 头部(header) | 可选,PyJWT 会根据算法自动生成 typ 和 alg |
自动生成 {"typ": "JWT", "alg": "HS256"} |
4.3 生成 HS256 JWT 的标准流程
使用 PyJWT 生成 Token 只需调用 jwt.encode() 方法。以下是完整的步骤与代码示例:
- 导入 PyJWT 模块
- 定义载荷(Payload):包含标准声明(Registered Claims)和自定义声明
- 设置密钥:确保密钥强度足够
- 调用
jwt.encode():传入载荷、密钥和算法 - 输出 Token:返回的为字节串,通常解码为字符串使用
import jwt
import time
# 1. 定义密钥(生产环境应从环境变量或密钥管理服务获取)
SECRET_KEY = "my-secure-hs256-key-which-is-at-least-32-bytes!"
# 2. 构建载荷(Payload)
payload = {
"sub": "user_1001", # 主题:用户标识
"name": "张三", # 自定义声明:用户名
"role": "admin", # 自定义声明:角色
"iat": int(time.time()), # 签发时间(当前时间戳)
"exp": int(time.time()) + 3600 # 过期时间:1 小时后
}
# 3. 生成 JWT Token
token = jwt.encode(
payload=payload,
key=SECRET_KEY,
algorithm="HS256"
)
# 4. 输出 Token(解码为字符串)
print("生成的 JWT Token:")
print(token.decode("utf-8") if isinstance(token, bytes) else token)
输出示例:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ1c2VyXzEwMDEiLCJuYW1lIjoi5byg5LiJIn0.8iYjKx5f6G7QzL9p0W1vN2mR3sT4uV5wX6yZ7aB8cD9
4.4 生成流程可视化
下图展示了从载荷定义到最终 Token 输出的完整生成链路:
4.5 关键配置与最佳实践
在实际项目中,生成 JWT 时需注意以下配置项与安全规范:
| 配置项 | 推荐做法 | 说明 |
|---|---|---|
| 密钥管理 | 从环境变量或密钥管理服务(KMS)读取 | 禁止硬编码在代码中,防止密钥泄露 |
| 过期时间(exp) | 根据业务场景设置,通常 15 分钟 ~ 24 小时 | 避免设置过长有效期,降低 Token 泄露风险 |
| 签发时间(iat) | 必须设置为当前时间戳 | 用于校验 Token 是否在有效期内 |
| 自定义声明 | 避免存放敏感信息(如密码、信用卡号) | JWT 载荷仅做 Base64URL 编码,并非加密 |
| Token 输出格式 | 解码为 UTF-8 字符串后返回给客户端 | PyJWT 返回字节串,需 .decode() 处理 |
4.6 常见错误与避坑指南
⚠️ 密钥长度不足: HS256 要求密钥至少 256 位(32 字节)。若密钥过短,PyJWT 会抛出
jwt.exceptions.InvalidKeyError。请务必使用足够长度的密钥。
- 错误: 载荷中未设置
exp字段,导致 Token 永不过期。 - 错误: 使用
jwt.encode()时误将algorithm写为"HS256"以外的值(如"none"),造成安全漏洞。 - 错误: 在载荷中存放密码等敏感信息,导致信息泄露。
- 正确做法: 始终在载荷中包含
exp、iat标准声明,并使用强密钥。
4.7 完整代码示例(含异常处理)
以下是一个包含异常处理的生产级生成函数:
import jwt
import time
from typing import Dict, Optional
def generate_hs256_token(
payload: Dict,
secret: str,
expires_in: int = 3600
) -> Optional[str]:
"""
生成 HS256 签名的 JWT Token
:param payload: 载荷字典(必须包含 sub)
:param secret: 对称密钥(至少 32 字节)
:param expires_in: 过期时间(秒),默认 1 小时
:return: JWT Token 字符串,失败返回 None
"""
try:
# 补充标准声明
payload["iat"] = int(time.time())
payload["exp"] = int(time.time()) + expires_in
# 生成 Token
token_bytes = jwt.encode(
payload=payload,
key=secret,
algorithm="HS256"
)
# 解码为字符串
return token_bytes.decode("utf-8") if isinstance(token_bytes, bytes) else token_bytes
except jwt.exceptions.InvalidKeyError as e:
print(f"密钥无效:{e}")
return None
except Exception as e:
print(f"生成 Token 失败:{e}")
return None
# 使用示例
if __name__ == "__main__":
secret = "my-very-secure-hs256-key-that-is-32-bytes-long!!"
user_payload = {
"sub": "user_1001",
"name": "张三",
"role": "admin"
}
token = generate_hs256_token(user_payload, secret)
if token:
print(f"生成的 Token:\n{token}")
生产建议: 将密钥存储在环境变量或 Vault 中,通过
os.getenv("JWT_SECRET") 读取。同时建议为不同服务使用不同的密钥,实现密钥隔离。