Flask-JWT-Extended刷新令牌后identity为None及签名验证失败问题
看起来你遇到了两个关联的棘手问题:刷新后的access token identity为None,以及refresh token解码时的签名验证失败。我来帮你逐个排查解决:
问题1:刷新后access token的identity为None
你的核心错误是错误混用了@jwt.expired_token_loader装饰器。这个装饰器的作用是注册一个回调函数,当access token过期时自动触发,而不是用来装饰刷新路由的。把它和@jwt_refresh_token_required、路由装饰器放在同一个函数上,会导致身份识别逻辑混乱,最终获取不到identity。
修正方案:
将过期token的回调与刷新路由分开定义:
# 先定义过期access token的回调函数 @jwt.expired_token_loader def handle_expired_token(expired_token): # 引导用户触发刷新逻辑 return redirect('/token/refresh') # 单独定义刷新路由 @app.route('/token/refresh', methods=['GET','POST']) @jwt_refresh_token_required def refresh(): current_user = get_jwt_identity() # 现在current_user应该能正确获取到身份了 access_token = create_access_token(identity=current_user) response = make_response(redirect(request.referrer)) # 跳回原请求页更合理 set_access_cookies(response, access_token) return response
这样拆分后,@jwt_refresh_token_required会正确解析refresh token中的身份信息,get_jwt_identity()就能拿到有效值,生成的新access token自然也会包含正确的identity。
问题2:refresh token解码时的InvalidSignatureError
这个错误通常和密钥不匹配或密钥格式错误有关,结合你提到在jwt.io中勾选base64编码密钥后验证通过的情况,大概率是代码中密钥的处理方式不对:
可能的原因与解决:
密钥格式不匹配:
如果你在jwt.io中是通过勾选"secret base64 encoded"才通过验证,说明你的实际密钥是base64编码后的字符串。但Flask-JWT-Extended默认会直接将JWT_SECRET_KEY作为原始密钥使用,不会自动解码base64。修正方法:在配置中解码密钥:
import base64 class BaseConfig(object): # 假设你的原始密钥是base64编码后的字符串,先解码 JWT_SECRET_KEY = base64.b64decode("你的base64编码密钥") JWT_ACCESS_TOKEN_EXPIRES = timedelta(minutes=10) JWT_REFRESH_TOKEN_EXPIRES = timedelta(minutes=1) JWT_TOKEN_LOCATION = 'cookies' JWT_COOKIE_CSRF_PROTECT = False密钥一致性问题:
确保生成token时使用的JWT_SECRET_KEY和解码时的完全一致,包括大小写、特殊字符等。如果中途修改过密钥,之前生成的refresh token会因为签名不匹配而报错,需要重新生成新的refresh token。Cookie传递问题:
检查refresh token的Cookie是否被正确传递到服务器,比如Cookie的path、domain配置是否和请求匹配,避免浏览器因为同源策略不发送Cookie。可以在配置中明确设置:JWT_ACCESS_COOKIE_PATH = '/' JWT_REFRESH_COOKIE_PATH = '/'
验证修正效果
完成上述调整后:
- 当access token过期时,
handle_expired_token会触发跳转至刷新路由; - 刷新路由通过
@jwt_refresh_token_required正确解析refresh token的身份; - 生成的新access token会包含正确的
identity; - refresh token的签名验证也会因为密钥匹配而通过。
内容的提问来源于Stack Exchange,提问作者Danny Diaz

