You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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内容(注意去掉打印语句中的单引号),确认是否存在空格、引号或特殊字符转义差异
  • 验证前端拼接的Authorization Header格式,确保是Bearer <token>(Bearer与Token之间有且仅有一个空格),可在后端打印完整的Authorization Header值确认格式正确性

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解码器与生成时使用相同密钥和算法的措施

  1. 统一配置来源:将密钥、算法等配置统一放在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']])
    
  2. 使用同一JWT库:全程使用flask_jwt_extended库的API(如@jwt_required()装饰器、get_jwt_identity()),避免混用不同JWT库,确保生成与解码逻辑一致。

  3. 验证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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.01 03:20:53