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

如何在Flask的JWT认证中添加Refresh Token(刷新令牌)

给JWT授权添加Refresh Token功能

核心思路

  • Access Token(访问令牌)保持短时效(比如15分钟),用于接口授权校验
  • Refresh Token(刷新令牌)设置更长时效(比如7天),专门用于获取新的Access Token
  • 必须存储Refresh Token,确保只有合法有效的令牌能换取新的Access Token

第一步:调整登录接口,生成双令牌

先修正原代码中JWT过期时间的不规范写法(原代码将过期时间转为字符串不符合JWT标准),同时生成Access Token和Refresh Token,并存储Refresh Token:

# 生产环境建议用Redis或数据库存储,这里用字典做临时示例
refresh_tokens = {}

@app.route("/login", methods=['POST'])
def login():
    request_json = request.get_json()
    username = request_json.get("username")
    if not username:
        return jsonify({"message": "用户名必填"}), 400

    # 生成Access Token(15分钟有效期)
    access_token_payload = {
        "user": username,
        "exp": datetime.datetime.utcnow() + datetime.timedelta(minutes=15)
    }
    access_token = jwt.encode(access_token_payload, str(app.config['SECRET_KEY']), algorithm='HS256')

    # 生成Refresh Token(7天有效期)
    refresh_token_payload = {
        "user": username,
        "exp": datetime.datetime.utcnow() + datetime.timedelta(days=7)
    }
    refresh_token = jwt.encode(refresh_token_payload, str(app.config['SECRET_KEY']), algorithm='HS256')

    # 存储Refresh Token,绑定用户名
    refresh_tokens[refresh_token] = username

    return jsonify({
        "access_token": access_token,
        "refresh_token": refresh_token,
        "token_type": "Bearer"
    })

第二步:实现Refresh Token刷新接口

创建接口接收Refresh Token,验证通过后返回新的Access Token:

@app.route("/refresh", methods=['POST'])
def refresh():
    request_json = request.get_json()
    refresh_token = request_json.get("refresh_token")
    if not refresh_token:
        return jsonify({"message": "refresh token 必填"}), 400

    # 先校验Refresh Token是否在存储中
    if refresh_token not in refresh_tokens:
        return jsonify({"message": "无效或已过期的refresh token"}), 401

    try:
        # 解码Refresh Token
        payload = jwt.decode(refresh_token, str(app.config['SECRET_KEY']), algorithms=['HS256'])
        username = payload.get("user")

        # 生成新的Access Token
        new_access_token_payload = {
            "user": username,
            "exp": datetime.datetime.utcnow() + datetime.timedelta(minutes=15)
        }
        new_access_token = jwt.encode(new_access_token_payload, str(app.config['SECRET_KEY']), algorithm='HS256')

        return jsonify({
            "access_token": new_access_token,
            "token_type": "Bearer"
        })
    except jwt.ExpiredSignatureError:
        # 过期则移除存储的Refresh Token
        del refresh_tokens[refresh_token]
        return jsonify({"message": "refresh token 已过期"}), 401
    except jwt.InvalidTokenError:
        return jsonify({"message": "无效的refresh token"}), 401

第三步:保留原有的Access Token验证装饰器

原token_required装饰器无需修改,依然用于保护需要授权的接口:

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

def token_required(func):
    @wraps(func)
    def decorated(*args, **kwargs):
        token = None
        if "x-access-token" in request.headers:
            token = request.headers['x-access-token']
        if not token:
            return jsonify({
                "message": "token is required"
            }), 401
        try:
            payload = jwt.decode(token, str(app.config['SECRET_KEY']), algorithms=['HS256'])
        except Exception as ee:
            print(ee)
            return jsonify({
                "message": "invalid token"
            }), 401
        return func(*args, **kwargs)
    return decorated

# 示例保护接口
@app.route("/protected", methods=['GET'])
@token_required
def protected():
    return jsonify({"message": "这是受保护的内容"})

生产环境注意事项

  • 不要用全局字典存储Refresh Token,改用Redis(支持自动过期)或数据库,避免服务重启后令牌丢失
  • 可以给Refresh Token添加jti(JWT唯一ID)字段,方便精准注销令牌
  • 用户修改密码或主动注销时,要及时从存储中删除对应的Refresh Token

内容的提问来源于stack exchange,提问作者Andrew L.

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.16 21:00:24