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() 方法。以下是完整的步骤与代码示例:

  1. 导入 PyJWT 模块
  2. 定义载荷(Payload):包含标准声明(Registered Claims)和自定义声明
  3. 设置密钥:确保密钥强度足够
  4. 调用 jwt.encode():传入载荷、密钥和算法
  5. 输出 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 输出的完整生成链路:

HS256 JWT 生成流程 载荷 (Payload) sub, name, exp, iat 密钥 (Secret) 至少 32 字节 算法 (HS256) HMAC-SHA256 签名计算 HMAC(header + payload) JWT Token (Base64URL 编码)

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") 读取。同时建议为不同服务使用不同的密钥,实现密钥隔离。