基于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获取逻辑才能支持请求头校验。
三、前端代码问题及修正
现有前端代码的问题
- 注册/登录请求中提前添加了
Authorization头,但此时还未拿到Token,会导致请求头值为Bearer undefined,无实际意义; - 登录请求的
type设为get,但后端接口仅接受POST请求,会导致请求失败; localStorage.setItem调用错误:登录代码缺少key参数,注册代码多余了JSON.parse(后端返回的Token是字符串,无需解析);- 注册/登录成功后未执行页面跳转,无法直接进入受保护的首页。
修正后的注册前端代码
$(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
相关产品推荐
相关产品推荐

