JWT的签发与验证组件
1. 组件整体说明
py-jwt-helper 是一个轻量级的 JWT 工具库,封装了密钥生成、JWT 签发、验签、载荷提取以及 JWKS 构建与解析等完整流程。
核心特性:
- 统一 Base64 密钥格式 — 对称密钥与非对称公私钥对外均以 Base64 字符串传递,内部按算法自动解析
- 五种签名算法 — HS256、RS256、SM3(SM3+HMAC)、SM2(SM3+SM2)、SM4(SM3+SM4)
- JWK/JWKS 标准兼容 — 支持 RFC 7517 格式的 JWKS 构建、解析与 kid 自动匹配
- 非侵入式国密扩展 — 通过 PyJWT 的
register_algorithm()注入国密算法,不修改 PyJWT 源码 - FastAPI 集成 — 提供多系统签发隔离的鉴权依赖注入
2. 组件依赖加载
安装
# 基础安装
pip install py-jwt-helper
# 如需 FastAPI 集成
pip install py-jwt-helper[fastapi]
运行时依赖
| 依赖 | 版本要求 | 用途 |
|---|---|---|
| PyJWT[crypto] | >= 2.8.0 | JWT 编解码与算法框架 |
| cryptography | >= 46.0.5 | RSA 密钥生成与 PEM 解析 |
| gmssl | >= 3.2.2 | 国密 SM2/SM3/SM4 算法实现 |
国密算法注册
使用 SM2、SM3、SM4 算法前,需先注册国密扩展:
from py_jwt_helper.jwt.extensions import register_sm_crypto_algorithms
register_sm_crypto_algorithms()
建议在应用启动时调用一次即可。
导入
from py_jwt_helper import JwtHelper, JwtPayload, JwkHelper, JwkKeyEntry
3. 核心接口功能说明
3.1. 算法支持与加签逻辑
3.1.1. 支持的算法说明
| 算法 | 类型 | 密钥长度 | 签名方式 | JWK kty |
|---|---|---|---|---|
| HS256 | 对称 | 32 字节 | HMAC-SHA256(key, msg) | oct |
| SM3 | 对称 | 32 字节 | HMAC-SM3(key, msg),与 HS256 结构相同 | oct |
| SM4 | 对称 | 16 字节 | SM4-CBC 加密取最后分组作 MAC | oct |
| RS256 | 非对称 | 2048 位 | RSASSA-PKCS1-v1_5,SHA-256哈希 + RSA私钥签名 | RSA |
| SM2 | 非对称 | 私钥 32 字节 / 公钥 64 字节 | SM3哈希 + SM2椭圆曲线签名 | EC (crv=SM2) |
- 对称算法:加签与验签使用同一密钥
- 非对称算法:私钥加签,公钥验签;SM2 由于内部
prepare_key兼容,私钥也可用于验签
3.1.2. 生成各算法的密钥的接口和结果说明
JwtHelper.generate_key_or_keypair(alg)
根据算法名生成密钥,统一返回 Base64 编码格式。
参数:
| 参数 | 类型 | 说明 |
|---|---|---|
alg |
str |
算法名称,支持 HS256、SM3、SM4、RS256、SM2 |
返回值:
- 对称算法(HS256 / SM3 / SM4):返回
str— Base64 编码的密钥字符串 - 非对称算法(RS256 / SM2):返回
Dict[str, str],结构为:
{
"private_key": "Base64编码的私钥",
"public_key": "Base64编码的公钥"
}
调用示例:
from py_jwt_helper import JwtHelper
from py_jwt_helper.jwt.extensions import register_sm_crypto_algorithms
register_sm_crypto_algorithms()
## 对称算法 — 返回单个 Base64 字符串
hs256_key = JwtHelper.generate_key_or_keypair("HS256")
## 例: "KU49TG81ii+VKtP2WV4/jaZGh4AOvyW5WUXcxnvsqMs="
sm3_key = JwtHelper.generate_key_or_keypair("SM3")
sm4_key = JwtHelper.generate_key_or_keypair("SM4")
# 非对称算法 — 返回 dict
rsa_keys = JwtHelper.generate_key_or_keypair("RS256")
# 例: {"private_key": "LS0tLS1CRUdJTi...", "public_key": "LS0tLS1CRUdJTi..."}
sm2_keys = JwtHelper.generate_key_or_keypair("SM2")
# 例: {"private_key": "IesuWFlC5RgjS7IX...", "public_key": "BHzn7neIZZX6t3VA..."}
JwtHelper.get_public_key_from_private(secret_key, alg)
基于非对称算法的私钥推导其对应的公钥。
参数:
| 参数 | 类型 | 说明 |
|---|---|---|
secret_key |
str |
Base64 编码的私钥字符串 |
alg |
str |
算法类型,仅支持 RS256、SM2 |
返回值: str — Base64 编码的公钥字符串
调用示例:
sm2_keys = JwtHelper.generate_key_or_keypair("SM2")
pub_key = JwtHelper.get_public_key_from_private(sm2_keys["private_key"], "SM2")
assert pub_key == sm2_keys["public_key"]
3.1.3. 基于各算法生成 JWT 的接口说明
JwtHelper.create_jwt(secret_key, sub, expire_second, ...)
创建一个完整的 JWT 字符串。
参数:
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
secret_key |
str |
是 | Base64 编码的密钥。对称算法为共享密钥;非对称算法为私钥 |
sub |
str |
是 | JWT 主题内容或标识,通常存放业务数据 |
expire_second |
int |
是 | 有效期(秒)。负值可生成已过期的 token 用于测试 |
iss |
str |
否 | 签发者身份标识 |
before_second |
int |
否 | 延迟生效时间(秒),生成 nbf 声明。必须小于 expire_second |
alg |
str |
否 | 签名算法,默认 "RS256" |
kid |
str |
否 | 密钥 ID,写入 JWT Header,用于 JWKS 场景中按 kid 匹配验签密钥 |
返回值: str — 编码后的 JWT 字符串
调用示例:
from py_jwt_helper import JwtHelper
# 对称算法签名
key = JwtHelper.generate_key_or_keypair("HS256")
token = JwtHelper.create_jwt(
secret_key=key,
sub="user_001",
expire_second=3600,
iss="my_service",
alg="HS256",
kid="hs256-key-1"
)
# 非对称算法签名(私钥签发)
rsa_keys = JwtHelper.generate_key_or_keypair("RS256")
token = JwtHelper.create_jwt(
secret_key=rsa_keys["private_key"],
sub="user_002",
expire_second=1800,
iss="auth_server",
alg="RS256",
kid="rsa-key-1"
)
# sub 中存放 JSON 业务数据
import json
payload_data = {"providerId": "hr", "id": "00001", "name": "张三"}
token = JwtHelper.create_jwt(
secret_key=rsa_keys["private_key"],
sub=json.dumps(payload_data, ensure_ascii=False),
expire_second=86400 * 30,
alg="RS256"
)
3.2. 验签相关逻辑
3.2.1. 多密钥/公钥 + kid 生成 JWKS 信息说明
JwkKeyEntry — JWKS 构建入参实体
| 字段 | 类型 | 说明 |
|---|---|---|
alg |
str |
算法名称(HS256 / SM3 / SM4 / RS256 / SM2) |
kid |
str |
密钥 ID,用于在 JWKS 中唯一标识该密钥 |
key |
str |
Base64 编码的验签密钥。对称算法为共享密钥,非对称算法为公钥 |
JwkHelper.build_jwks(keys)
根据传入的验签密钥列表构建标准的 JWKS(JSON Web Key Set)结构。
参数:
| 参数 | 类型 | 说明 |
|---|---|---|
keys |
List[JwkKeyEntry] |
JwkKeyEntry 列表 |
返回值: Dict — 标准 JWKS 结构
{
"keys": [
{
"alg": "HS256",
"kid": "hs256-1",
"use": "sig",
"kty": "oct",
"k": "<Base64Url 编码的对称密钥>"
},
{
"alg": "RS256",
"kid": "rsa-1",
"use": "sig",
"kty": "RSA",
"n": "<Base64Url 编码的模数>",
"e": "<Base64Url 编码的指数>"
},
{
"alg": "SM2",
"kid": "sm2-1",
"use": "sig",
"kty": "EC",
"crv": "SM2",
"x": "<Base64Url 编码的 X 坐标>",
"y": "<Base64Url 编码的 Y 坐标>"
}
]
}
调用示例:
from py_jwt_helper import JwtHelper, JwkHelper, JwkKeyEntry
# 生成各算法密钥
hs256_key = JwtHelper.generate_key_or_keypair("HS256")
sm3_key = JwtHelper.generate_key_or_keypair("SM3")
rsa_keys = JwtHelper.generate_key_or_keypair("RS256")
sm2_keys = JwtHelper.generate_key_or_keypair("SM2")
# 构建入参列表(非对称算法使用公钥)
entries = [
JwkKeyEntry(alg="HS256", kid="hs256-1", key=hs256_key),
JwkKeyEntry(alg="SM3", kid="sm3-1", key=sm3_key),
JwkKeyEntry(alg="RS256", kid="rsa-1", key=rsa_keys["public_key"]),
JwkKeyEntry(alg="SM2", kid="sm2-1", key=sm2_keys["public_key"]),
]
jwks = JwkHelper.build_jwks(entries)
# jwks 可直接作为 JSON 响应暴露到 /.well-known/jwks.json 端点
3.2.2. 基于带有 kid 的 JWT 或 JWKS 中匹配验签密钥/公钥的说明
JwkHelper.get_key_from_jwk(jwk)
从单个 JWK 字典中提取验签密钥。
参数:
| 参数 | 类型 | 说明 |
|---|---|---|
jwk |
Dict |
单个 JWK 字典(非 JWKS,需从 jwks["keys"][i] 获取) |
返回值: str — Base64 编码的验签密钥字符串
JwkHelper.resolve_key_from_jwks(jwks, token)
根据 JWT Header 中的 kid 从 JWKS 中自动匹配对应的 JWK,并提取验签密钥。
参数:
| 参数 | 类型 | 说明 |
|---|---|---|
jwks |
Dict |
标准 JWKS 字典 {"keys": [...]} |
token |
str |
JWT 字符串(Header 中需包含 kid) |
返回值: str — Base64 编码的验签密钥字符串
调用示例:
from py_jwt_helper import JwtHelper, JwkHelper, JwkKeyEntry
# 1. 构建多密钥 JWKS
rsa_keys = JwtHelper.generate_key_or_keypair("RS256")
sm2_keys = JwtHelper.generate_key_or_keypair("SM2")
entries = [
JwkKeyEntry(alg="RS256", kid="rsa-1", key=rsa_keys["public_key"]),
JwkKeyEntry(alg="SM2", kid="sm2-1", key=sm2_keys["public_key"]),
]
jwks = JwkHelper.build_jwks(entries)
# 2. 用私钥签发带 kid 的 JWT
token = JwtHelper.create_jwt(
secret_key=rsa_keys["private_key"],
sub="user_001",
expire_second=3600,
alg="RS256",
kid="rsa-1"
)
# 3. 根据 token 中的 kid 自动匹配并提取验签密钥
resolved_key = JwkHelper.resolve_key_from_jwks(jwks, token)
# resolved_key 将匹配 kid="rsa-1" 对应的公钥
# 4. 验签
payload = JwtHelper.verify_jwt(token, resolved_key)
3.2.3. JWT 验签和 Payload 信息获取的结构说明
JwtHelper.verify_jwt(token, secret_key)
校验 JWT 的签名有效性、过期时间、生效时间等。校验失败会抛出 jwt.exceptions.PyJWTError 的相关子类。
参数:
| 参数 | 类型 | 说明 |
|---|---|---|
token |
str |
JWT 字符串 |
secret_key |
str |
Base64 编码的验签密钥。对称算法为共享密钥;非对称算法为公钥 |
返回值: JwtPayload — 载荷实体对象
可能抛出的异常:
| 异常类 | 说明 |
|---|---|
jwt.exceptions.DecodeError |
token 格式错误或 Header 缺少算法 |
jwt.exceptions.ExpiredSignatureError |
token 已过期 |
jwt.exceptions.ImmatureSignatureError |
token 尚未生效(nbf) |
jwt.exceptions.InvalidSignatureError |
签名不匹配 |
JwtHelper.extract_payload(token)
忽略签名,仅提取 JWT 的 Payload 部分(适用于不需要验签的调试场景)。
参数:
| 参数 | 类型 | 说明 |
|---|---|---|
token |
str |
JWT 字符串 |
返回值: JwtPayload — 载荷实体对象
JwtPayload — 载荷实体
继承自 dict,可直接作为字典使用,同时提供以下便捷属性:
| 属性 | 类型 | 说明 |
|---|---|---|
exp_time |
datetime | None |
过期时间(UTC),对应 exp 声明 |
iat_time |
datetime | None |
签发时间(UTC),对应 iat 声明 |
nbf_time |
datetime | None |
生效时间(UTC),对应 nbf 声明 |
iss |
str | None |
签发者,对应 iss 声明 |
sub |
str | None |
主题内容,对应 sub 声明 |
所有标准 JWT 声明及自定义字段均可通过字典访问,如 payload["sub"]、payload.get("custom_field")。
调用示例:
from py_jwt_helper import JwtHelper
# 验签并获取 payload
try:
payload = JwtHelper.verify_jwt(token, public_key)
print(payload.sub) ## "user_001"
print(payload.iss) ## "auth_server"
print(payload.exp_time) ## datetime(2026, 5, 26, 12, 0, 0, tzinfo=UTC)
print(payload.iat_time) ## datetime(2026, 5, 25, 12, 0, 0, tzinfo=UTC)
print(payload["sub"]) ## 字典方式访问,等价于 payload.sub
except jwt.exceptions.ExpiredSignatureError:
print("Token 已过期")
except jwt.exceptions.InvalidSignatureError:
print("签名无效")
# 仅提取 payload(不验签)
payload = JwtHelper.extract_payload(token)
print(payload.sub)
完整端到端示例:
from py_jwt_helper import JwtHelper, JwkHelper, JwkKeyEntry
from py_jwt_helper.jwt.extensions import register_sm_crypto_algorithms
register_sm_crypto_algorithms()
# 1. 生成密钥
rsa_keys = JwtHelper.generate_key_or_keypair("RS256")
# 2. 构建入参并生成 JWKS
entries = [JwkKeyEntry(alg="RS256", kid="rsa-1", key=rsa_keys["public_key"])]
jwks = JwkHelper.build_jwks(entries)
# 3. 签发 JWT
token = JwtHelper.create_jwt(
secret_key=rsa_keys["private_key"],
sub="user_001",
expire_second=3600,
iss="auth_server",
alg="RS256",
kid="rsa-1"
)
# 4. 从 JWKS 中自动匹配密钥并验签
resolved_key = JwkHelper.resolve_key_from_jwks(jwks, token)
payload = JwtHelper.verify_jwt(token, resolved_key)
print(f"用户: {payload.sub}, 签发者: {payload.iss}, 过期: {payload.exp_time}")
Release files for py-jwt-helper 0.1.2
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| py_jwt_helper-0.1.2.tar.gz | 12.2 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| py_jwt_helper-0.1.2-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 28.7 kB
Release files / py_jwt_helper-0.1.2.tar.gz
| Download URL | py_jwt_helper-0.1.2.tar.gz |
|---|---|
| Size | 12.2 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
5fc3594deaf6ec0a7f9f0fedc1c5950cd7cf26459a56d537533b348767d59bf0
|
|
BLAKE2b-256 checksum How to use checksums |
bb0cb946dfd95933366402174d86ce1873af2365f72aeee8a7118a15e1ec2a27
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.8.8
|
Release files / py_jwt_helper-0.1.2-py3-none-any.whl
| Download URL | py_jwt_helper-0.1.2-py3-none-any.whl |
|---|---|
| Size | 16.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
ddc9fb8a68b1e5c304740f2a03843a57aa8203ce8d89bf2a71f198acbcccb364
|
|
BLAKE2b-256 checksum How to use checksums |
a073632b2df925f25b294134f328e6bcde9fae23c62b36eefa78e7a712fee97e
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.8.8
|