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

HMAC refresh token刷新代码示例及切换JWT解决过期问题的疑问

问题解答

切换到JWT是否能解决你的问题?

是的,切换到JWT完全可以解决你当前的问题。JWT的访问令牌(Access Token)设计为短期有效期(比如2分钟),配合长期有效的Refresh Token(比如30天),当Access Token过期时,客户端可以用Refresh Token向专门的接口请求新的Access Token,无需用户重新登录。

你当前用HMAC遇到的问题,核心是没有一套成熟的令牌刷新机制——每次Access Token过期都需要重新发起认证流程,而如果认证流程本身耗时或者令牌有效期过短,就容易出现请求前令牌已过期的情况。JWT的Refresh Token机制正好填补了这个空白,让令牌刷新流程自动化且无需用户参与。

HMAC实现Refresh Token的示例代码(Python)

以下是一个基于HMAC的Refresh Token实现示例,包含令牌生成、验证、刷新逻辑:

依赖安装

pip install cryptography python-dotenv

核心代码

import hmac
import hashlib
import time
from datetime import datetime, timedelta
from typing import Optional, Dict
import os
from dotenv import load_dotenv

load_dotenv()
SECRET_KEY = os.getenv("HMAC_SECRET_KEY").encode()
ACCESS_TOKEN_EXPIRE_MINUTES = 2
REFRESH_TOKEN_EXPIRE_DAYS = 30

# 模拟数据库存储Refresh Token(实际项目用真实数据库)
refresh_token_store: Dict[str, Dict] = {}

def generate_hmac_token(payload: Dict) -> str:
    """生成带HMAC签名的令牌"""
    # 将载荷转为有序字符串(避免签名因键顺序变化失效)
    payload_str = "&".join([f"{k}={v}" for k, v in sorted(payload.items())])
    # 生成HMAC签名
    signature = hmac.new(SECRET_KEY, payload_str.encode(), hashlib.sha256).hexdigest()
    # 返回格式:载荷字符串|签名
    return f"{payload_str}|{signature}"

def verify_hmac_token(token: str) -> Optional[Dict]:
    """验证HMAC令牌并解析载荷"""
    try:
        payload_str, signature = token.split("|", 1)
        # 重新计算签名并校验(用compare_digest避免时序攻击)
        computed_signature = hmac.new(SECRET_KEY, payload_str.encode(), hashlib.sha256).hexdigest()
        if not hmac.compare_digest(signature, computed_signature):
            return None
        # 解析载荷
        payload = {}
        for item in payload_str.split("&"):
            k, v = item.split("=", 1)
            payload[k] = int(v) if k == "exp" else v
        # 检查令牌是否过期
        if payload.get("exp", 0) < time.time():
            return None
        return payload
    except:
        return None

def generate_access_and_refresh_tokens(user_id: str) -> Dict[str, str]:
    """生成Access Token和Refresh Token"""
    # 生成短期Access Token
    access_exp = int((datetime.now() + timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)).timestamp())
    access_payload = {"user_id": user_id, "exp": access_exp, "type": "access"}
    access_token = generate_hmac_token(access_payload)

    # 生成长期Refresh Token
    refresh_exp = int((datetime.now() + timedelta(days=REFRESH_TOKEN_EXPIRE_DAYS)).timestamp())
    refresh_payload = {"user_id": user_id, "exp": refresh_exp, "type": "refresh"}
    refresh_token = generate_hmac_token(refresh_payload)

    # 存储Refresh Token(用于后续校验有效性)
    refresh_token_store[refresh_token] = {"user_id": user_id, "exp": refresh_exp}

    return {
        "access_token": access_token,
        "refresh_token": refresh_token,
        "access_expires_in": ACCESS_TOKEN_EXPIRE_MINUTES * 60,
        "refresh_expires_in": REFRESH_TOKEN_EXPIRE_DAYS * 86400
    }

def refresh_access_token(refresh_token: str) -> Optional[Dict[str, str]]:
    """用Refresh Token刷新Access Token"""
    # 验证Refresh Token的签名和格式
    payload = verify_hmac_token(refresh_token)
    if not payload or payload.get("type") != "refresh":
        return None

    # 检查存储中的Refresh Token是否有效(防止已注销或过期)
    stored_token = refresh_token_store.get(refresh_token)
    if not stored_token or stored_token["exp"] < time.time() or stored_token["user_id"] != payload["user_id"]:
        return None

    # 生成新的Access Token(可选择同时生成新的Refresh Token)
    return generate_access_and_refresh_tokens(payload["user_id"])

# 示例用法
if __name__ == "__main__":
    # 生成初始令牌对
    tokens = generate_access_and_refresh_tokens("user_123")
    print("初始令牌:", tokens)

    # 等待3分钟(模拟Access Token过期)
    time.sleep(180)

    # 刷新Access Token
    new_tokens = refresh_access_token(tokens["refresh_token"])
    if new_tokens:
        print("刷新后的令牌:", new_tokens)
    else:
        print("Refresh Token无效或已过期")

关键说明

  • 令牌格式采用载荷字符串|HMAC签名,载荷必须包含exp(过期时间戳)和type(区分access/refresh令牌)
  • Refresh Token需要后端持久化存储(示例用字典模拟,实际推荐用Redis或关系型数据库),用于验证有效性和防止重复使用
  • 签名校验必须使用hmac.compare_digest,避免时序攻击导致的签名泄露

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.31 20:46:34