Python Flask应用JWT处理逻辑及客户端传入令牌的校验相关问题
Flask 服务端JWT校验完整处理流程
一、整体处理步骤
- 第一步:提取请求头中的令牌
首先要从Authorization请求头里把JWT摘出来,规范格式是Bearer <token>,所以要做字符串切割,去掉前缀Bearer拿到纯token字符串,还要处理头不存在、格式不对的情况,直接返回401未授权。 - 第二步:令牌格式基础校验
先判断拿到的字符串是不是符合JWT的三段式结构(头.载荷.签名),缺段、格式非法直接返回401。 - 第三步:核心有效性校验
这步不是只校验secret_key这么简单,要校验几个核心维度:- 签名合法性:用服务端保存的
secret_key对前两段重新加密,和第三段签名做比对,不一致说明token被篡改,直接无效。 - 过期时间校验:校验载荷里的
exp字段,当前时间已经超过exp值的话,token已过期,返回401。 - 生效时间校验(可选):如果载荷里加了
nbf(not before)字段,要校验当前时间是否晚于nbf,早于的话token还没生效。 - 发行方/受众校验(可选):如果生成时加了
iss(发行方)、aud(受众)字段,也要对应校验匹配,避免其他服务签发的token被滥用。
- 签名合法性:用服务端保存的
- 第四步:载荷信息提取与业务校验
校验通过后,从载荷里把用户ID、角色等自定义信息取出来,还要做业务层校验,比如用户是否还存在、是否被封禁、权限是否匹配当前接口要求,没问题再放行到业务逻辑。
二、具体实现方式
1. 基于Flask-JWT-Extended扩展的实现(推荐,无需手动造轮子)
首先安装扩展:pip install flask-jwt-extended
核心代码示例:
from flask import Flask, jsonify from flask_jwt_extended import JWTManager, jwt_required, get_jwt_identity app = Flask(__name__) # 配置secret_key,必须和生成token时的密钥一致 app.config["JWT_SECRET_KEY"] = "your-secret-key-never-expose-in-production" jwt = JWTManager(app) # 需要校验token的接口直接加@jwt_required()装饰器即可,扩展会自动完成上面所有校验步骤 @app.route("/protected", methods=["GET"]) @jwt_required() def protected(): # 直接获取载荷里的用户标识 current_user = get_jwt_identity() return jsonify(logged_in_as=current_user), 200
扩展已经默认做了签名校验、过期校验,要是需要自定义校验规则,比如校验用户状态,可以用@jwt.token_in_blocklist_loader装饰器实现黑名单校验,或者自定义回调函数。
2. 原生手动校验实现(适合理解原理)
可以用PyJWT库实现:pip install pyjwt
代码示例:
import jwt from flask import request, jsonify SECRET_KEY = "your-secret-key-never-expose-in-production" def verify_jwt(): # 提取token auth_header = request.headers.get("Authorization") if not auth_header or not auth_header.startswith("Bearer "): return None, "无效的Authorization头" token = auth_header.split(" ")[1] try: # 校验核心参数,verify_signature默认开启,会自动用secret_key校验签名 payload = jwt.decode( token, SECRET_KEY, algorithms=["HS256"], # 自动校验exp、nbf等字段,不需要自己写时间判断 options={"verify_exp": True} ) return payload, None except jwt.ExpiredSignatureError: return None, "令牌已过期" except jwt.InvalidSignatureError: return None, "令牌签名无效" except jwt.InvalidTokenError: return None, "无效令牌" # 接口中使用 @app.route("/protected", methods=["GET"]) def protected(): payload, err = verify_jwt() if err: return jsonify({"msg": err}), 401 # 后续业务逻辑 return jsonify(logged_in_as=payload.get("user_id")), 200
三、常见注意事项
- 不是只校验secret_key就可以:只校验签名只能保证token没被篡改,但没法判断token是否过期、是否是发给当前服务的,还有业务层面的用户状态也需要校验。
- 生产环境注意:secret_key要存在环境变量里,不要硬编码到代码里;敏感信息不要放到JWT载荷里,因为载荷是base64编码的,任何人拿到token都可以解码看到内容。
内容的提问来源于stack exchange,提问作者max
相关产品推荐
相关产品推荐

