PyJWT手动生成验证token最简路径是显式指定algorithm、设置exp并校验签名;需用装饰器封装jwt.decode,捕获所有异常,且SECRET_KEY必须一致。

Flask 中用 PyJWT 手动生成和验证 token 最简路径
不用扩展也能做,PyJWT 本身足够轻量。关键不是“能不能”,而是“别漏掉 exp 和 verify_signature”。很多人本地测试能过,一上生产就报 InvalidTokenError 或静默失败,基本都是签名密钥没对齐、时间戳未校验、或算法写错。
安装后直接用:
pip install PyJWT
生成 token 示例(含必要字段):
import jwt
import datetime
<p>SECRET_KEY = "your-secret-key" # 生产环境务必从环境变量读取
payload = {
"user_id": 123,
"username": "alice",
"exp": datetime.datetime.utcnow() + datetime.timedelta(hours=1)
}
token = jwt.encode(payload, SECRET_KEY, algorithm="HS256")
</p>
-
algorithm必须显式指定,PyJWT >= 2.0已移除默认算法,不写会报TypeError: encode() missing 1 required positional argument: 'algorithm' -
exp强烈建议加上,否则 token 永不过期;验证时需主动传options={"verify_exp": True},否则不会检查 -
SECRET_KEY在 Flask 多进程或重启后必须保持一致,否则旧 token 全部失效
Flask 路由里验证 token 的典型写法
别在每个视图里重复写 jwt.decode,封装成装饰器最稳。重点是捕获所有可能异常,而不是只 catch InvalidTokenError。
from functools import wraps
from flask import request, jsonify
<p>def require_jwt(f):
@wraps(f)
def decorated(*args, **kwargs):
auth_header = request.headers.get("Authorization")
if not auth_header or not auth_header.startswith("Bearer "):
return jsonify({"error": "Missing or invalid Authorization header"}), 401</p><pre class="brush:php;toolbar:false;"> token = auth_header[7:] # 去掉 "Bearer " 前缀
try:
payload = jwt.decode(
token,
SECRET_KEY,
algorithms=["HS256"],
options={"verify_exp": True} # 显式启用过期检查
)
except jwt.ExpiredSignatureError:
return jsonify({"error": "Token expired"}), 401
except jwt.InvalidTokenError:
return jsonify({"error": "Invalid token"}), 401
except Exception:
return jsonify({"error": "Token decode failed"}), 401
request.current_user = payload # 注入到 request 上下文
return f(*args, **kwargs)
return decorated- HTTP Header 必须是
Authorization: Bearer <token></token>,前端漏空格或大小写错误(如bearer)都会导致解析失败 -
algorithms参数必须是列表(["HS256"]),传字符串会报TypeError: expected list - 不要把
payload直接当用户对象用——里面字段没做服务端校验,比如user_id是否真实存在,得查库确认
为什么不用 Flask-JWT-Extended?
它确实省事,但隐含行为多:自动刷新逻辑、固定 cookie 名、强制使用 current_user 全局变量。如果你只需要登录后发一个短期 token 给前端存 localStorage,再带过来校验,那 PyJWT + 手写装饰器更透明、更容易调试。
容易踩的坑:
- Flask-JWT-Extended 默认用
datetime.utcnow()校验exp,但如果你的服务器时区不是 UTC,且没配JWT_ACCESS_TOKEN_EXPIRES为timedelta,token 会提前失效 - 它的
@jwt_required()不校验fresh字段,除非你显式加@jwt_required(fresh=True),而这个字段需要额外调create_fresh_token - 升级到 v4+ 后,
get_jwt_identity()返回的是解码后的整个 payload,不是原先的 user_id —— 如果代码里直接int(get_jwt_identity())就会崩
调试 token 时最该看的三件事
token 看似一串乱码,其实可分三段 Base64Url 解码(用在线工具或 jwt.decode(token, options={"verify_signature": False}))。真正出问题时,先盯住:
- Header 段里的
alg值是不是HS256(别是none或空) - Payload 段里的
exp是不是 Unix 时间戳,且比当前时间大(注意时区!) - 签名段是否被篡改 —— 改任意字符都会让
jwt.decode报InvalidSignatureError,不是InvalidTokenError
签名密钥哪怕多一个空格、换行符,或者用了 bytes 而不是 str,验证都会失败,且错误信息几乎一样,只能靠打印 repr(SECRET_KEY) 排查。











