最轻量的Flask API Token防护是用@token_required装饰器校验Bearer Token:从Authorization头提取→用PyJWT验证签名和exp→失败返回401,成功才放行;需捕获ExpiredSignatureError、InvalidTokenError等异常,避免500崩溃。

用 @token_required 装饰器拦截无Token请求
没加校验的 Flask API 接口,只要知道 URL 就能直接调用,极易被爬虫或恶意脚本高频请求。最轻量、最常用的防护方式就是加一层 Token 校验装饰器——它不依赖第三方库,也不改路由定义,只在视图函数执行前做判断。
核心逻辑是:从 Authorization 请求头提取 Bearer Token → 解析并验证签名/过期时间 → 验证通过才放行,否则返回 401 Unauthorized。
实操建议:
- Token 建议用
PyJWT生成和校验,安装命令:pip install PyJWT - 务必使用密钥(
SECRET_KEY)签名,不要硬编码在代码里,应从环境变量读取:os.environ.get("JWT_SECRET") - 校验时必须检查
exp字段,否则过期 Token 仍可使用 - 装饰器内不要直接抛异常,统一返回 JSON 响应,例如:
return jsonify({"error": "Invalid or expired token"}), 401
处理 Token 解析失败的常见错误类型
实际运行中,jwt.decode() 会因多种原因抛出异常,如果没捕获,整个请求会 500 崩溃,反而暴露服务细节。必须显式覆盖这些典型错误:
立即学习“Python免费学习笔记(深入)”;
-
jwt.ExpiredSignatureError:Token 已过期,应返回401 -
jwt.InvalidTokenError:签名不匹配或格式非法(如少字符、含空格),也返回401 -
jwt.DecodeError:Base64 解码失败,常见于前端传错格式(比如漏了Bearer前缀),同样归为认证失败 - 其他异常(如
KeyError缺少user_id字段)建议记录日志但不暴露细节,统一返回模糊提示
示例片段(关键部分):
try:
data = jwt.decode(token, SECRET_KEY, algorithms=["HS256"])
current_user_id = data.get("user_id")
except jwt.ExpiredSignatureError:
return jsonify({"error": "Token expired"}), 401
except (jwt.InvalidTokenError, jwt.DecodeError):
return jsonify({"error": "Invalid token"}), 401
为什么不能只校验 Token 存在,还要查数据库?
仅验证 JWT 签名和有效期,只能确认 Token “格式合法”,但无法判断该用户是否已被禁用、权限是否变更、或 Token 是否被主动注销(如用户登出)。这时候需要二次校验。
实操建议:
- 对高敏感接口(如支付、删数据),在
@token_required内部追加一次数据库查询,检查user_id对应的is_active和status字段 - 避免每次请求都查库:可用 Redis 缓存用户状态,设置与 Token 过期时间一致的 TTL;若查缓存未命中再查 DB 并回填
- 注销 Token 的标准做法不是删 JWT(它已签发),而是把
user_id + jti(JWT 唯一标识)写入黑名单集合,校验时先查黑名单
前端传 Token 时容易漏掉的两个细节
后端装饰器写得再严谨,前端传错格式也会导致校验永远失败。最常见的两个低级但高频问题:
- 请求头写成
headers: { "Authorization": "token abc123" }—— 缺少Bearer前缀,正确应为"Bearer abc123" - Token 字符串末尾带换行或空格(尤其从调试工具复制时),导致
jwt.decode()直接抛DecodeError;建议在解析前用.strip()清理
可在装饰器开头加一行防御性处理:
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
token = auth_header[7:].strip() # 去掉 "Bearer " 并清理空格
Token 校验本身不难,真正复杂的是状态同步、黑名单维护和错误反馈粒度——这些地方一旦忽略,防护就形同虚设。


















