多标签页JWT刷新踩坑

脚本小子阿杰 专家 7小时前 更新于 2026年7月26日 598 浏览 13 点赞 约 1 分钟

单页应用(SPA)里最容易被忽略的坑就是:用户习惯开很多个标签页。如果你在Axios拦截器里写了isRefreshing这种内存锁,在单个页面里确实能跑通,但一旦用户开了4个Tab,Token过期那一刻,这4个页面会同时触发刷新请求。

如果后端开启了“刷新令牌轮转(Refresh Token Rotation)”,也就是用一次新Token就会让旧的失效,那结果就是:Tab 1刷新成功,Tab 2紧随其后刷新,直接把Tab 1刚拿到的Token给废了。最后用户在所有页面被强制踢回登录页,极其崩溃。

核心问题就在于:JavaScript的内存空间是隔离的。 Tab A里的isRefreshing = true,Tab B根本看不见。

要解决这种跨标签页的并发冲突,得用浏览器原生的 Web Locks API (navigator.locks),它能让同源的不同标签页竞争同一个锁。

实操方案如下:

一、引入 Web Locks 逻辑
不再依赖简单的布尔值,而是请求一个名为 auth_refresh_lock 的排他锁。

async function refreshToken() {
  // 请求一个跨标签页的独占锁
  return await navigator.locks.request('auth_refresh_lock', async () => {
    // 1. 先检查 localStorage,看是不是已经有其他标签页刷新好了
    const latestToken = localStorage.getItem('access_token');
    if (isTokenValid(latestToken)) {
      return latestToken; 
    }

    // 2. 真正发起网络请求
    try {
      const response = await axios.post('/auth/refresh-token/', {
        refresh_token: localStorage.getItem('refresh_token')
      });
      const { accessToken } = response.data;
      
      // 3. 存入存储,让其他等待锁的标签页能直接拿到
      localStorage.setItem('access_token', accessToken);
      return accessToken;
    } catch (error) {
      throw error;
    }
  });
}

二、集成到 Axios 拦截器
在响应拦截器里,当捕获到 401 错误时,调用上述带锁的刷新函数。

axios.interceptors.response.use(
  res => res,
  async error => {
    if (error.response?.status === 401) {
      try {
        // 这里会排队,只有拿到锁的 Tab 才会发请求
        const newToken = await refreshToken(); 
        error.config.headers['Authorization'] = `Bearer ${newToken}`;
        return axios(error.config);
      } catch (refreshError) {
        window.location.href = '/login';
      }
    }
    return Promise.reject(error);
  }
);

这个方案的精髓在于:

  • 互斥执行: 无论开了多少个Tab,同一时间只有一个在请求 /refresh-token/
  • 结果共享: 后到者拿到锁后,先检查 localStorage,发现 Token 已经更新了,直接拿来用,无需再次请求。

这种处理方式比之前折腾的内存队列硬核得多,也稳得多。建议所有做大模型 AI Agent 工作流前端的同学都检查一下自己的 Auth 逻辑,别让用户在多窗口操作时莫名其妙掉线。
AI编程AI编程实战webdevreactjavascript

全部回复 (3)

咖啡续命折腾党 中级 13小时前
可以用 BroadcastChannel 播报一下,让其他页面同步新 token。
0 回复
脚本小子阿杰 专家 13小时前
我也被这坑搞过,后来改用共享worker才勉强解决。
0 回复
副业中测试 中级 13小时前
得把Token存LocalStorage,不然刷个页面就得重新登录。
0 回复

发表回复

支持 Markdown 格式