在 Python 中处理 JWT(JSON Web Token) 时,PyJWT 和 python-jose[cryptography] 是两个主流库,它们功能相似但定位和生态不同。下面我将从 功能、安全性、使用场景、FastAPI 兼容性 等角度为你详细对比,并给出明确建议。
🔑 一、核心结论(先看这个)
| 项目 | 推荐使用 |
|---|---|
| 新项目(尤其是 FastAPI) | ✅ python-jose[cryptography] |
| 简单 JWT 签发/验证(无 JWE/JWK) | ⚠️ PyJWT 也可用 |
| 需要 JWE(加密 JWT)、JWK(密钥集)等完整 JOSE 支持 | ✅ 必须用 python-jose |
| FastAPI 官方文档示例 | ✅ 使用 python-jose |
💡 一句话总结:
如果你用 FastAPI 做认证(如 OAuth2、JWT 登录),直接选python-jose[cryptography],它是社区事实标准。
📦 二、库简介
1. PyJWT
- GitHub: https://github.com/jpadilla/pyjwt
- 安装:
pip install PyJWT 特点:
- 轻量级,仅支持 JWS(签名 JWT)
- 不支持 JWE(加密 JWT)
- 默认不包含加密后端(需额外安装
cryptography)
示例:
import jwt token = jwt.encode({"sub": "123"}, "secret", algorithm="HS256")
2. python-jose
- GitHub: https://github.com/mpdavis/python-jose
安装:
pip install "python-jose[cryptography]"[cryptography]表示包含加密后端(基于cryptography库)
特点:
- 实现完整的 JOSE 规范(JWS + JWE + JWK)
- 支持 RSA、ECDSA、HMAC 等多种算法
- 被 FastAPI 官方教程 采用
示例:
from jose import jwt token = jwt.encode({"sub": "123"}, "secret", algorithm="HS256")
🔍 注意:两者 API 非常相似(因为都遵循 RFC 7519),但 不是完全兼容!
🔍 三、详细对比
| 特性 | PyJWT | python-jose[cryptography] |
|---|---|---|
| JWS(签名) | ✅ | ✅ |
| JWE(加密) | ❌ | ✅ |
| JWK(密钥集) | ❌ | ✅ |
| 算法支持 | HMAC, RSA, ECDSA | 更全面(包括 AES-GCM 等 JWE 算法) |
| FastAPI 官方推荐 | ❌ | ✅ |
| 依赖大小 | 较小(+ cryptography) | 较大(自带完整 JOSE 实现) |
| 错误信息友好度 | 一般 | 更清晰(如 JWTError, ExpiredSignatureError) |
| Python 类型提示 | 较弱 | 较好 |
| 维护活跃度 | 活跃 | 活跃 |
🛡 四、安全性与依赖
两者都依赖 cryptography 库做底层加密
所以安装时通常都要:
pip install PyJWT[crypto] # 或手动 pip install cryptography pip install "python-jose[cryptography]"
⚠️ 不要用纯 Python 实现的算法(如PyJWT不带cryptography)—— 性能差且不安全!
🧪 五、代码示例对比
场景:签发和验证 HS256 JWT
✅ PyJWT
import jwt
from jwt.exceptions import ExpiredSignatureError, InvalidTokenError
SECRET_KEY = "my-secret"
ALGORITHM = "HS256"
# 签发
token = jwt.encode(
{"sub": "user123", "exp": 1734567890},
SECRET_KEY,
algorithm=ALGORITHM
)
# 验证
try:
payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
except ExpiredSignatureError:
print("Token expired")
except InvalidTokenError:
print("Invalid token")✅ python-jose
from jose import jwt, ExpiredSignatureError, JWTError
SECRET_KEY = "my-secret"
ALGORITHM = "HS256"
# 签发
token = jwt.encode(
{"sub": "user123", "exp": 1734567890},
SECRET_KEY,
algorithm=ALGORITHM
)
# 验证
try:
payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
except ExpiredSignatureError:
print("Token expired")
except JWTError:
print("Invalid token")🔁 几乎一样!但注意异常类名不同:
- PyJWT:
InvalidTokenError- python-jose:
JWTError
🚀 六、为什么 FastAPI 官方用 python-jose?
FastAPI 的 官方安全教程 明确使用:
from jose import jwt原因:
- 历史选择:早期 PyJWT 对异步/类型提示支持较弱
- 错误分类更细:
ExpiredSignatureError、JWTClaimsError等便于处理 - 社区惯性:大量 FastAPI 项目沿用此方案,生态工具(如
fastapi-users)也基于它
📌 如果你用 FastAPI,保持一致性比“哪个更好”更重要。
🧩 七、高级功能:只有 python-jose 支持
1. JWE(加密 JWT)
from jose import jwe
encrypted = jwe.encrypt("payload", key, encryption="A256GCM")
decrypted = jwe.decrypt(encrypted, key)2. JWK(从公钥 URL 加载密钥)
from jose import jwk
keys = jwk.get_jwk_keys_from_url("https://example.com/.well-known/jwks.json")这在对接 Auth0、Firebase、AWS Cognito 等第三方 IDP 时非常有用。
🛑 八、常见陷阱
1. 混淆两个库的 API
# 错误!混用会导致奇怪问题
import jwt # 可能是 PyJWT
from jose import jwt as jose_jwt # 又引入另一个 jwt✅ 正确做法:只选一个,全项目统一
2. 忘记安装加密后端
# PyJWT 必须显式安装 crypto 支持
pip install PyJWT[crypto]
# python-jose 必须带 [cryptography]
pip install "python-jose[cryptography]"否则会报错:Algorithm not supported
✅ 九、最终建议
| 你的场景 | 推荐 |
|---|---|
| FastAPI / Starlette 项目 | ✅ python-jose[cryptography] |
| 简单脚本、内部工具 | ⚠️ PyJWT[crypto] 也可以 |
| 需要 JWE / JWK / 企业级 OIDC | ✅ python-jose[cryptography] |
| 追求最小依赖 | ⚠️ PyJWT(但差别不大) |
🔥 对于你的项目(FastAPI + Tortoise-ORM + 用户认证):
毫不犹豫选择python-jose[cryptography]—— 它是 FastAPI 生态的标准。
📦 十、安装命令(复制即用)
# 推荐:python-jose(带加密支持)
pip install "python-jose[cryptography]"
# 备选:PyJWT(带加密支持)
pip install "PyJWT[crypto]"然后在代码中:
# FastAPI 风格(推荐)
from jose import jwt, JWTError, ExpiredSignatureError
评论已关闭