Flask中JWT生成解码不匹配致/get_user_infos_from_token返回401如何解决?
问题排查与解决方案
问题背景
调用/get_user_infos_from_token接口时返回401错误,观察到生成的Token与解码时的Token看似不一致,已完成以下检查:
- 确认localStorage中的Token字符串与
create_access_token生成结果完全一致 - 确认Token生成与解码使用的密钥相同
- 确认编码和解码使用的算法一致
额外排查修复操作
1. 检查Token传输与格式问题
- 对比后端
login接口和get_user_infos_from_token接口打印的Token内容(注意去掉打印语句中的单引号),确认是否存在空格、引号或特殊字符转义差异 - 验证前端拼接的
AuthorizationHeader格式,确保是Bearer <token>(Bearer与Token之间有且仅有一个空格),可在后端打印完整的AuthorizationHeader值确认格式正确性
2. 验证Token有效性与过期时间
- 使用JWT解码工具(如本地打开jwt.io)解析Token,查看
exp字段对应的UTC时间是否正常,排查服务器与客户端的时钟偏移问题(JWT过期校验依赖UTC时间)
3. 细化异常捕获,定位具体错误原因
后端当前用全局except捕获所有异常,无法定位错误类型(如Header缺失、Token格式错误、签名无效等),修改代码打印异常详情:
@app.route('/get_user_infos_from_token', methods=['GET']) def get_user_infos_from_token(): try: authorization_header = request.headers.get('Authorization') if not authorization_header or not authorization_header.startswith('Bearer '): return jsonify({'error': 'Invalid Authorization header format'}), 401 token = authorization_header.split(" ")[1] print("Token: '" + str(token) + "'") decoded_token = jwt.decode(token, app.config['JWT_SECRET_KEY'], algorithms=['HS256']) user_id = decoded_token['identity'] user = UserController().find_user_by_user_id(user_id) if user is None: return jsonify({'error': 'User not found'}), 404 return jsonify(user), 200 except Exception as e: print(f"Decoding error: {str(e)}") # 打印具体异常信息 return jsonify({'error': f'Invalid token: {str(e)}'}), 401
4. 排查跨域请求(CORS)问题
前端请求域名(localhost)与后端(127.0.0.1)存在跨域差异,需确保后端配置CORS允许传递Authorization Header:
from flask_cors import CORS app = Flask(__name__) CORS(app, supports_credentials=True) # 允许跨域携带凭证和自定义Header
5. 统一JWT库的使用方式
当前后端使用flask_jwt_extended生成Token,但用PyJWT解码,可能存在库实现差异。建议统一使用flask_jwt_extended的内置解码机制:
from flask_jwt_extended import jwt_required, get_jwt_identity, JWTManager app = Flask(__name__) app.config['JWT_SECRET_KEY'] = 'example-key' jwt = JWTManager(app) # 初始化JWT管理器 @app.route('/get_user_infos_from_token', methods=['GET']) @jwt_required() # 自动校验Token,无效则返回401 def get_user_infos_from_token(): try: user_id = get_jwt_identity() # 直接获取生成Token时的identity user = UserController().find_user_by_user_id(user_id) if user is None: return jsonify({'error': 'User not found'}), 404 return jsonify(user), 200 except Exception as e: print(f"Error: {str(e)}") return jsonify({'error': str(e)}), 401
确保JWT解码器与生成时使用相同密钥和算法的措施
统一配置来源:将密钥、算法等配置统一放在Flask配置中,生成和解码均从该配置读取,避免硬编码或拼写错误:
app.config['JWT_SECRET_KEY'] = 'example-key' app.config['JWT_ALGORITHM'] = 'HS256' # 显式指定算法,默认也是HS256生成和解码时直接引用配置项:
# 生成Token(自动使用配置的密钥和算法) access_token = create_access_token(identity=user['user_id'], expires_delta=timedelta(days=7)) # 解码时使用相同配置 decoded_token = jwt.decode(token, app.config['JWT_SECRET_KEY'], algorithms=[app.config['JWT_ALGORITHM']])使用同一JWT库:全程使用
flask_jwt_extended库的API(如@jwt_required()装饰器、get_jwt_identity()),避免混用不同JWT库,确保生成与解码逻辑一致。验证Token签名:用JWT解码工具输入Token和密钥,确认签名验证通过,且算法显示为
HS256。
原始代码参考
Flask后端代码
app = Flask(__name__) app.config.from_object(__name__) app.config['JWT_SECRET_KEY'] = 'example-key' @app.route('/login', methods=['POST']) def login(): # Extract the username and password from the request user_email = request.get_json(silent=True).get('user_email', None) user_password = request.get_json(silent=True).get('user_password', None) # Validate the credentials using the UserController user = UserController().find_user_by_user_email_and_password(user_email, user_password) if user is False: return jsonify({'login': False}), 401 # Calculate the expiration time exp = datetime.utcnow() + timedelta(days=7) # Create a JWT token with the user identity and expiration time access_token = create_access_token(identity=user['user_id'], expires_delta=timedelta(days=7)) print("Token: '" + str(access_token) + "'") # Return the JWT token in the response return jsonify({'access_token': access_token}), 200 @app.route('/get_user_infos_from_token', methods=['GET']) def get_user_infos_from_token(): try: authorization_header = request.headers.get('Authorization') token = authorization_header.split(" ")[1] print("Token: '" + str(token) + "'") decoded_token = jwt.decode(token, app.config['JWT_SECRET_KEY'], algorithms=['HS256']) user_id = decoded_token['identity'] user = UserController().find_user_by_user_id(user_id) if user is None: return jsonify({'error': 'User not found'}), 404 return jsonify(user), 200 except: return jsonify({'error': 'Invalid token'}), 401
前端登录代码
async login(){ let self = this; axios.post('http://127.0.0.1:5000/login', { user_email: self.email , user_password: self.password }) .then(function (response) { localStorage.setItem('token', response.data.access_token); console.log(response.data) self.login_error = ''; window.alert('Uhu, deu certo! Vou te redirecionar para a página principal.'); self.$router.push({path: '/'}); }) .catch(function (error) { self.login_error = 'Usuário ou senha incorretos. Tente novamente.'; console.log(error); }); },
前端获取用户信息代码
mounted(){ if (localStorage.getItem('token') != null == true){ let self = this; const token = localStorage.getItem('token').toString(); console.log(token); axios.get('http://localhost:5000/get_user_infos_from_token', { headers: { Authorization: `Bearer ${token}` } }).then(response => { self.user_data = response.data; }).catch(error => { console.error(error); }); localStorage.setItem('user_data', self.user_data); self.user_auth_flag = true; } },
内容的提问来源于stack exchange,提问作者Victor Moraes
相关产品推荐
相关产品推荐

