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

基于Flask+JQuery的JWT授权实现问题求助

Flask + JQuery JWT授权实现问题及解决方案

一、后端现有代码

Token校验装饰器

def token_required(func):
    @wraps(func)
    def decorated(*args,**kwargs):
        token = request.args.get('token')
        if not token:
            return jsonify({'Alert!' : 'Token is missing!'})
        try:
            payload = jwt.decode(token,SECRET_KEY)
        except:
            return jsonify({'Alert!' : 'Invalid token!'})
        return func(*args,**kwargs)
    return decorated

受保护路由

@app.route('/')
@token_required
def home():
    return "You are logged in"

注册接口

@app.route('/registerUser',methods=['GET', 'POST'])
def registerUser():
    conn = mysql.connect()
    cursor = conn.cursor()
    if request.method == 'POST':
        data = request.get_json()
        username = data['username']
        email = data['email']
        password = data['password']
        confirm_password = data['confirmPassword']

        if password == confirm_password:
            password = generate_password_hash( data['password'],method="sha256")
            if isRegistered(email):
                return make_response("Email already exists",409)
            else:
                query_string = "INSERT INTO users_eg(username,email,password)VALUES(%s,%s,%s);"
                cursor.execute(query_string, (username,email,password))
                conn.commit()
                token = jwt.encode({
                    'user':data['email'],
                    'expiration': str(datetime.utcnow() + timedelta(minutes=30))
                },
                SECRET_KEY)
                return make_response(jsonify({'token' : token}),201) # 需要跳转至首页
        else:
            return make_response("Check confirm password again",400)

登录接口

@app.route('/loginUser',methods=['GET', 'POST'])
def loginUser():
    if request.method == 'POST':
        data = request.get_json()
        email = data['email']
        password = data['password']
        if isRegistered(email):
            if checkPassword(password):       
                token = jwt.encode({
                    'user':data['email'],
                    'expiration': str(datetime.utcnow() + timedelta(minutes=30))
                },
                SECRET_KEY)

                return make_response(jsonify({'token' : token}),200) # 需要跳转至首页
            else:
                return make_response("Password is incorrect",400)
        else:
            return make_response("Email doesn't exist",400)

二、核心疑问解答

1. Token如何用于访问受保护路由?

是的,必须在前端完成以下操作:

  • 注册/登录成功后,从接口返回的result.token中获取Token,存储到localStorage或sessionStorage;
  • 后续请求受保护路由时,将Token放入请求头的Authorization字段,格式为Bearer <你的token>,不建议通过URL参数传递(存在安全风险)。

2. 请求头发送Token后,现有装饰器会自动校验吗?

不会。当前装饰器仅从URL参数request.args.get('token')获取Token,完全未处理请求头的Authorization字段,必须修改装饰器的Token获取逻辑才能支持请求头校验。

三、前端代码问题及修正

现有前端代码的问题

  1. 注册/登录请求中提前添加了Authorization头,但此时还未拿到Token,会导致请求头值为Bearer undefined,无实际意义;
  2. 登录请求的type设为get,但后端接口仅接受POST请求,会导致请求失败;
  3. localStorage.setItem调用错误:登录代码缺少key参数,注册代码多余了JSON.parse(后端返回的Token是字符串,无需解析);
  4. 注册/登录成功后未执行页面跳转,无法直接进入受保护的首页。

修正后的注册前端代码

$(document).ready(function () {
    $("form").on("submit", function (event) {
        const userInfo = {
            username: $("#username").val(),
            email: $("#email").val(),
            password: $("#password").val(),
            confirmPassword: $("#confirmPassword").val(),
        };
        $.ajax({
            type: "post",
            url: "http://127.0.0.1:5000/registerUser",
            dataType: "json",
            contentType: "application/json",
            data: JSON.stringify(userInfo),
            success: function (result) {
                localStorage.setItem("token", result.token);
                // 注册成功后跳转首页
                window.location.href = "/";
            },
            error: function(xhr) {
                alert(xhr.responseText);
            }
        });
        event.preventDefault();
    });
});

修正后的登录前端代码

$(document).ready(function () {
    $("form").on("submit", function (event) {
        const userInfo = {
            email: $("#email").val(),
            password: $("#password").val(),
        };
        $.ajax({
            type: "post",
            url: "http://127.0.0.1:5000/loginUser",
            dataType: "json",
            contentType: "application/json",
            data: JSON.stringify(userInfo),
            success: function (result) {
                localStorage.setItem("token", result.token);
                // 登录成功后跳转首页
                window.location.href = "/";
            },
            error: function(xhr) {
                alert(xhr.responseText);
            }
        });
        event.preventDefault();
    });
});

四、后端代码优化

1. 修改Token校验装饰器(支持请求头校验)

from flask import request, jsonify
from functools import wraps
import jwt

def token_required(func):
    @wraps(func)
    def decorated(*args,**kwargs):
        auth_header = request.headers.get('Authorization')
        if not auth_header:
            return jsonify({'Alert!' : 'Token is missing!'}), 401
        
        # 解析Authorization头,格式必须是Bearer <token>
        try:
            token_type, token = auth_header.split()
            if token_type.lower() != 'bearer':
                return jsonify({'Alert!' : 'Invalid token type!'}), 401
        except ValueError:
            return jsonify({'Alert!' : 'Invalid Authorization header format!'}), 401
        
        try:
            # 指定加密算法,自动校验过期(前提是后端生成Token时用了exp字段)
            payload = jwt.decode(token, SECRET_KEY, algorithms=["HS256"])
        except jwt.ExpiredSignatureError:
            return jsonify({'Alert!' : 'Token has expired!'}), 401
        except jwt.InvalidTokenError:
            return jsonify({'Alert!' : 'Invalid token!'}), 401
        
        # 可以将用户信息传递给路由函数
        return func(current_user=payload['user'], *args,**kwargs)
    return decorated

2. 优化Token生成逻辑(使用JWT自带过期校验)

原来的expiration字段是字符串,无法自动校验过期,建议改用JWT标准的exp声明:

from datetime import datetime, timedelta
import jwt

# 注册/登录接口中生成Token的代码替换为:
token = jwt.encode({
    'user': data['email'],
    'exp': datetime.utcnow() + timedelta(minutes=30)  # 自动校验过期
}, SECRET_KEY, algorithm="HS256")

# 如果PyJWT返回bytes类型,需要转成字符串:
# token = token.decode('utf-8')

return make_response(jsonify({'token' : token}), 201)  # 注册用201,登录用200

3. 受保护路由获取用户信息(可选)

修改后的装饰器可以传递用户信息给路由:

@app.route('/')
@token_required
def home(current_user):
    return f"You are logged in as {current_user}"

内容的提问来源于stack exchange,提问作者zyus

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.24 08:09:53