Spring Boot JWT Token 无感刷新(理论)

一、核心概念先理清 

在做前后端分离的登录鉴权时,我们通常会用到两种 Token:

表格

Token 类型作用有效期安全性典型场景
Access Token用于接口鉴权,携带在请求头中,验证用户身份短(如 15 分钟~2 小时)高,泄露后影响范围小每次请求接口时携带
Refresh Token用于在 Access Token 过期时,换取新的 Access Token长(如 7 天~30 天)较低,泄露后可长期续登仅在刷新 Token 时使用

为什么要分两种 Token?

  • 如果只用一个 Token,且有效期很长:一旦泄露,攻击者可以长期冒用身份,风险极高。
  • 如果只用一个 Token,且有效期很短:用户需要频繁登录,体验极差。
  • 双 Token 方案:短有效期 Access Token 保证安全,长有效期 Refresh Token 保证体验,兼顾安全与便捷。

二、完整生命周期流程 

我们把用户从登录到最终重新登录的完整流程拆解为 4 个阶段:

阶段 1:首次登录,获取双 Token

  1. 用户输入账号密码,前端调用 /user/login 接口。
  2. 后端验证账号密码合法后,生成:
    • accessToken:短有效期,用于接口鉴权。
    • refreshToken:长有效期,用于刷新 Access Token。
  3. 后端将双 Token 及过期时间返回给前端(通常放在响应体 data 中)。
  4. 前端将双 Token 存储到本地(如 localStoragesessionStorage 或安全的 Cookie)。
// 登录接口返回示例
{
  "code": 200,
  "msg": "登录成功",
  "data": {
    "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
    "refreshToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
    "accessExpire": "1720000000000",
    "refreshExpire": "1720604800000",
    "userId": 1,
    "username": "admin"
  }
}

阶段 2:正常使用,Access Token 未过期

  1. 前端每次请求接口时,在请求头 Authorization 中携带 accessToken

    http

    Authorization: Brear eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
    
  2. 后端过滤器 / 拦截器验证 accessToken 有效,放行请求,接口正常返回数据。
  3. 用户无感知,流畅使用软件。

阶段 3:Access Token 过期,自动刷新

accessToken 过期时,有两种常见的刷新方案:

方案 A:后端过滤器自动刷新(对前端透明)
  1. 前端携带过期的 accessToken 请求接口。
  2. 后端过滤器捕获 ExpiredJwtException,检测到 accessToken 过期。
  3. 过滤器从请求头读取 refreshToken,验证其有效性:
    • refreshToken 有效:生成新的 accessTokenrefreshToken,写入响应头:
      Access-Token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
      Refresh-Token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
      Access-Control-Expose-Headers: Access-Token,Refresh-Token
      
    • refreshToken 无效 / 过期:返回 401,提示用户重新登录。
  4. 前端在响应拦截器中读取响应头的新 Token,覆盖本地存储。
  5. 前端用新 accessToken 重新发起刚才失败的请求,用户完全无感知。
方案 B:前端拦截器主动刷新(更可控)
  1. 前端请求接口,收到 401 响应,判断为 accessToken 过期。
  2. 前端调用 /user/refresh/token 接口,携带本地存储的 refreshToken

    http

    POST /user/refresh/token
    Content-Type: application/x-www-form-urlencoded
    refreshToken=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
    
  3. 后端验证 refreshToken 有效后,返回新的双 Token。
  4. 前端更新本地存储的 Token,并重试刚才的请求。
  5. 若刷新接口也返回 401(refreshToken 过期),清除本地 Token 并跳转到登录页。

阶段 4:Refresh Token 过期,必须重新登录

  1. refreshToken 也过期时,无论哪种刷新方案,后端都会返回 401:

    json

    {
      "code": 401,
      "msg": "登录已过期,请重新登录",
      "data": null
    }
    
  2. 前端捕获 401 响应,清除本地存储的所有 Token。
  3. 前端跳转到登录页面,提示用户重新输入账号密码登录。
  4. 回到阶段 1,重新获取双 Token。


三、关键知识点总结

1. Token 过期逻辑

  • Access Token 过期:可以用 refreshToken 刷新,用户无感知。
  • Refresh Token 过期:无法刷新,必须重新登录。
  • 每次刷新时,建议生成新的 refreshToken,实现「活跃用户无限续期」:只要用户在 refreshToken 有效期内使用软件,就能一直续登,直到长期不使用导致 refreshToken 过期。

2. 安全注意事项

  • 传输安全:必须使用 HTTPS,防止 Token 在传输过程中被窃听。
  • 存储安全:前端避免将 Token 存在 localStorage(易受 XSS 攻击),推荐存在 HttpOnly Cookie 或使用 secure + sameSite 配置。
  • 泄露处理:若 refreshToken 泄露,攻击者可长期续登,建议后端实现 refreshToken 黑名单机制,登出 / 改密时将旧 refreshToken 加入黑名单。
  • 最小权限refreshToken 仅用于刷新 Token,不能用于接口鉴权。

3. 前端实现要点

  • 请求拦截器:统一在请求头添加 Authorization: {tokenPrefix} {accessToken}
  • 响应拦截器
    1. 捕获 401 响应,判断是否为 Token 过期。
    2. 若为 accessToken 过期,调用刷新接口。
    3. 刷新成功后,更新本地 Token 并重试原请求。
    4. 刷新失败(refreshToken 过期),跳转到登录页。
  • Header 读取:若使用后端过滤器自动刷新,需从响应头读取 Access-TokenRefresh-Token,注意配置 Access-Control-Expose-Headers 让前端可读取。

4. 后端实现要点

  • Token 生成accessToken 包含用户身份信息(如 userIdusername),refreshToken 仅包含用户名即可。
  • Token 验证
    • 验证 accessToken 时,若过期则尝试用 refreshToken 刷新。
    • 验证 refreshToken 时,若过期则直接拒绝,要求重新登录。
  • 接口设计
    • /user/login:生成双 Token。
    • /user/refresh-token:用 refreshToken 换取新双 Token。
    • /user/token/check:仅解析验证 Token,不生成新 Token。
  • 过滤器 / 拦截器:统一处理 Token 验证和刷新逻辑,避免在每个接口中重复实现。

四、前端响应拦截器代码

以下是基于 Axios 的前端响应拦截器实现,处理 Token 自动刷新

import axios from 'axios';

const request = axios.create({
  baseURL: '/api',
  timeout: 5000
});

// 请求拦截器:添加 Authorization
request.interceptors.request.use(config => {
  const accessToken = localStorage.getItem('accessToken');
  if (accessToken) {
    config.headers.Authorization = `Brear ${accessToken}`;
  }
  return config;
});

// 响应拦截器:处理 Token 过期和刷新
request.interceptors.response.use(
  response => {
    // 从响应头读取新 Token(后端过滤器自动刷新场景)
    const newAccessToken = response.headers['access-token'];
    const newRefreshToken = response.headers['refresh-token'];
    if (newAccessToken && newRefreshToken) {
      localStorage.setItem('accessToken', newAccessToken);
      localStorage.setItem('refreshToken', newRefreshToken);
    }
    return response.data;
  },
  async error => {
    const { response } = error;
    if (response && response.status === 401) {
      const msg = response.data?.msg;
      // 情况 1:refreshToken 也过期,必须重新登录
      if (msg.includes('登录已过期')) {
        localStorage.removeItem('accessToken');
        localStorage.removeItem('refreshToken');
        window.location.href = '/login';
        return Promise.reject(error);
      }

      // 情况 2:accessToken 过期,尝试刷新
      const refreshToken = localStorage.getItem('refreshToken');
      if (!refreshToken) {
        window.location.href = '/login';
        return Promise.reject(error);
      }

      try {
        // 调用刷新接口
        const res = await axios.post('/user/refresh-token', { refreshToken });
        const { token, refreshToken: newRefreshToken } = res.data.data;
        // 更新本地 Token
        localStorage.setItem('accessToken', token);
        localStorage.setItem('refreshToken', newRefreshToken);
        // 重试原请求
        error.config.headers.Authorization = `Brear ${token}`;
        return request(error.config);
      } catch (refreshErr) {
        // 刷新失败(refreshToken 过期),跳登录
        localStorage.removeItem('accessToken');
        localStorage.removeItem('refreshToken');
        window.location.href = '/login';
        return Promise.reject(refreshErr);
      }
    }
    return Promise.reject(error);
  }
);

export default request;

五、总结 ✨

双 Token 无感刷新是现代 Web 应用的标准鉴权方案,核心逻辑是:

  1. 短有效期 Access Token 保证接口鉴权安全。
  2. 长有效期 Refresh Token 保证用户体验,避免频繁登录。
  3. Access Token 过期时,用 Refresh Token 自动刷新,用户无感知。
  4. Refresh Token 过期时,强制重新登录,保证账号安全。

只要实现了这套流程,用户就能在「一直使用软件」的情况下,永远不需要手动重新登录,直到长期不使用导致 Refresh Token 自然过期,完美平衡了安全性用户体验

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值