基于 Access Token 和 Refresh Token实现无感刷新

双 Token 原理概述

  • Access Token:短期有效(如 30 分钟),用于接口鉴权,存放用户身份与权限信息。
  • Refresh Token:长期有效(如 7 天),仅用于在 Access Token 过期后“换取”新的两个 Token。
  • 无感刷新:用户在 Access Token 过期后,客户端自动携带 Refresh Token 请求刷新,服务器验证 Refresh Token 后下发新的 Access/Refresh Token,用户无须重新登录。

登录与颁发流程

  1. 客户端提交用户名/密码 → 服务端验证。

  2. 验证通过后:

    • 创建 Access Token,设置 30 分钟过期;
    • 创建 Refresh Token,设置 7 天过期;
  3. 将两者封装返回给客户端:

    1
    2
    3
    4
    5
    6
    {
    "accessToken": "eyJhbGci…",
    "accessExpire": 162,
    "refreshToken": "eyJhbGc…",
    "refreshExpire": 162
    }

访问拦截与刷新流程

接口访问

  • 客户端在每次请求 Authorization: Bearer <accessToken>

  • 服务端拦截器解析 Access Token:

    • 未过期 → 正常放行;
    • 已过期 → 返回特定状态码 401/511。

无感刷新

  • 客户端拦截到 401/511 → 同步或异步调用刷新接口
  • 服务端验证 Refresh Token:
    • 合法且未过期 → 重新生成 Access & Refresh Token 并返回;
    • 非法或已过期 → 返回 403 → 客户端跳转登录。
  • 客户端更新本地存储中的两个 Token,并重试原请求。

安全性与性能考虑

  • 防重放:Refresh Token 一旦使用即作废,服务端持久化黑名单或单次使用策略。
  • 漏洞防护:将 Refresh Token 存放在 HttpOnly Cookie 中,避免 XSS 泄露。
  • 并发刷新:客户端应保证同一时刻只有一次刷新调用,避免请求风暴。
  • 性能监控:统计刷新接口调用量,异常时及时报警。

为什么 Authorization 头要写成 Bearer <token>

这是HTTP 标准规定的身份验证格式。HTTP 协议里,Authorization 头支持多种认证方式,服务器必须知道你用的是哪一种。

常见的认证类型(服务器必须区分)

  • Basic:基础认证(账号密码 base64)
  • Digest:摘要认证
  • Bearer:OAuth2 / JWT 令牌认证
  • AWS4-HMAC-SHA256:AWS 签名
  • 等等…

HttpOnly Cookie 到底是什么?

HttpOnly Cookie = 浏览器给你存的、JS 完全碰不到的、最安全的身份凭证

  • 后端发给浏览器,前端无感

    1
    2
    3
    4
    5
    6
    Cookie cookie = new Cookie("token", "你的JWT令牌");
    cookie.setHttpOnly(true); // 核心
    cookie.setSecure(true);
    cookie.setPath("/");
    cookie.setMaxAge(86400);
    response.addCookie(cookie);
  • 浏览器自动保存

  • JS 代码读不到、改不了 → 防 XSS 偷 token

  • 下次发请求浏览器自动带上 → 不用写代码加请求头

  • 后端验证它

只能是refreshToken设置HttpOnly ,因为前端需要访问到accessToken,来判定过期时间等。