最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

前端實現(xiàn)無感刷新Token的方法與避坑指南

 更新時間:2026年03月25日 08:43:40   作者:前端Hardy  
實現(xiàn)token無感刷新對于前端來說是一項非常常用的技術(shù),今天,我們就來徹底搞懂如何真正實現(xiàn)無感刷新Token以及為什么?90%?的實現(xiàn)都有致命缺陷

刷新 Token 不是“過期就重新登錄”,而是讓用戶毫無感知地繼續(xù)使用

可惜,大多數(shù)項目還在用 401 跳登錄 粗暴處理——這根本不是用戶體驗,這是放棄治療。

在現(xiàn)代 Web 應(yīng)用中,用戶登錄后通常會獲得一對 Token:

  • Access Token(短期有效,如 15 分鐘)
  • Refresh Token(長期有效,如 7 天)

當(dāng) Access Token 過期時,理想狀態(tài)是:前端自動用 Refresh Token 換取新 Token,并重試原請求——整個過程用戶無感,頁面不跳轉(zhuǎn)、操作不中斷。

但現(xiàn)實呢?

“Token 過期 → 彈出登錄框 → 用戶罵一句‘怎么又登出了’ → 關(guān)掉頁面走人。”

今天,我們就來徹底搞懂:如何真正實現(xiàn)“無感刷新”Token?為什么 90% 的實現(xiàn)都有致命缺陷?

錯誤做法一:在每個接口里手動判斷 401

// 千萬別這么寫!
fetch('/api/user')
  .then(res => {
    if (res.status === 401) {
      // 重新登錄 or 刷新 token?
      window.location.href = '/login';
    }
  });

問題在哪?

  • 每個接口都要重復(fù)寫邏輯;
  • 如果多個請求同時 401,會觸發(fā)多次刷新,甚至多次跳登錄;
  • 完全無法做到“無感”。

錯誤做法二:全局?jǐn)r截 401 后直接刷新 Token 并重試一次

這是目前最“主流”的錯誤方案:

// 偽代碼:看似聰明,實則危險
axios.interceptors.response.use(
  res => res,
  async (error) => {
    if (error.response.status === 401) {
      const newToken = await refreshToken(); // 獲取新 token
      saveToken(newToken);
      
      // 用新 token 重試原請求
      return axios(error.config);
    }
  }
);

表面看沒問題,但隱藏三大坑

坑 1:并發(fā)請求雪崩

當(dāng)頁面剛加載,10 個接口同時發(fā)起,而此時 Token 已過期 ——10 個請求全部返回 401 → 觸發(fā) 10 次 refreshToken() → 后端收到 10 個刷新請求!

后果:

  • 后端可能拒絕重復(fù)刷新(安全策略);
  • Refresh Token 被提前消耗,后續(xù)真失效;
  • 用戶反而被踢下線。

坑 2:Refresh Token 泄露風(fēng)險

如果前端把 Refresh Token 存在 localStorage,一旦 XSS 攻擊成功,攻擊者可長期盜用賬號。

安全最佳實踐:Refresh Token 應(yīng)僅存于 HttpOnly Cookie,前端不可讀!

但上述方案要求前端“拿到新 token”,這就逼你把 Refresh Token 暴露給 JS —— 安全與功能不可兼得?

坑 3:無限重試死循環(huán)

如果 refreshToken() 本身也返回 401(比如 Refresh Token 也過期了),重試原請求 → 又 401 → 再刷新 → 再 401 → ……

瀏覽器卡死,內(nèi)存飆升。

正確方式:用“鎖機制 + 隊列 + 安全存儲”三位一體

要實現(xiàn)真正的無感刷新,必須同時解決:

  • 并發(fā)控制(只刷一次)
  • 安全存儲(Refresh Token 不暴露給 JS)
  • 失敗兜底(Refresh 失敗時優(yōu)雅降級)

第一步:后端配合 —— Refresh Token 存 HttpOnly Cookie

HTTP/1.1 200 OK
Set-Cookie: refreshToken=abc123; HttpOnly; Secure; SameSite=Strict; Path=/auth

前端永遠拿不到 refreshToken,但每次請求會自動攜帶。

第二步:前端實現(xiàn)“單例刷新鎖 + 請求隊列”

let isRefreshing = false;
let refreshPromise = null;
const failedQueue = [];

// 重試隊列中的請求
const processQueue = (error, token = null) => {
  failedQueue.forEach(({ resolve, reject }) => {
    if (error) {
      reject(error);
    } else {
      resolve(token);
    }
  });
  failedQueue.length = 0;
};

axios.interceptors.response.use(
  response => response,
  async (error) => {
    const originalRequest = error.config;

    if (error.response?.status === 401 && !originalRequest._retry) {
      if (isRefreshing) {
        // 已在刷新中,將請求加入隊列,等待新 token
        return new Promise((resolve, reject) => {
          failedQueue.push({ resolve, reject });
        }).then(token => {
          originalRequest.headers['Authorization'] = `Bearer ${token}`;
          return axios(originalRequest);
        });
      }

      originalRequest._retry = true;
      isRefreshing = true;

      try {
        // 調(diào)用刷新接口(后端從 Cookie 讀 refreshToken)
        const { data } = await axios.post('/auth/refresh');
        const newAccessToken = data.accessToken;

        // 通知所有排隊的請求
        processQueue(null, newAccessToken);

        // 重試當(dāng)前請求
        originalRequest.headers['Authorization'] = `Bearer ${newAccessToken}`;
        return axios(originalRequest);
      } catch (refreshError) {
        // 刷新失?。呵蹇毡镜厣矸?,跳轉(zhuǎn)登錄
        clearAuth();
        processQueue(refreshError, null);
        window.location.href = '/login';
        return Promise.reject(refreshError);
      } finally {
        isRefreshing = false;
        refreshPromise = null;
      }
    }

    return Promise.reject(error);
  }
);

關(guān)鍵設(shè)計解析

機制作用
isRefreshing 鎖確保同一時間只發(fā)起一次刷新
failedQueue 隊列緩存所有因 401 失敗的請求,等新 token 到手后批量重試
_retry 標(biāo)記防止重試后的請求再次進入刷新邏輯
HttpOnly Cookie保護 Refresh Token 不被 XSS 竊取

安全補充:前端 Token 存儲建議

Token 類型推薦存儲方式原因
Access Token內(nèi)存(JS 變量)或 sessionStorage短期有效,避免持久化泄露
Refresh TokenHttpOnly Cookie前端不可讀,防 XSS

切勿將任何 Token 存入 localStorage!這是 XSS 攻擊的黃金目標(biāo)。

如何測試你的刷新邏輯

  • 手動將 Access Token 設(shè)為過期;
  • 快速點擊多個按鈕,觸發(fā)并發(fā)請求;
  • 觀察 Network 面板:
    • 是否只調(diào)用了一次 /auth/refresh?
    • 所有原請求是否最終成功?
  • 模擬 Refresh Token 失效,是否跳轉(zhuǎn)登錄?

結(jié)語

“無感刷新 Token”不是炫技,而是對用戶體驗和系統(tǒng)安全的基本尊重。那些讓用戶頻繁重新登錄的產(chǎn)品,不是技術(shù)做不到,而是沒把用戶當(dāng)回事。

真正的專業(yè),藏在細節(jié)里:一個鎖、一個隊列、一個 HttpOnly Cookie —— 就是 10% 正確方案 與 90% 錯誤實現(xiàn)的分水嶺。

你的項目還在用“401 就跳登錄”嗎?是時候升級了。

歡迎轉(zhuǎn)發(fā)給那個總說“Token 過期就讓用戶重新登錄”的同事。

到此這篇關(guān)于前端實現(xiàn)無感刷新Token的方法與避坑指南的文章就介紹到這了,更多相關(guān)前端無感刷新token內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 微信小程序的宿主環(huán)境實現(xiàn)代碼

    微信小程序的宿主環(huán)境實現(xiàn)代碼

    這篇文章主要介紹了微信小程序的宿主環(huán)境,包括scroll-view 組件的基本使用,text 組件的基本使用及rich-text 組件的基本使用,本文通過示例代碼給大家介紹的非常詳細,需要的朋友可以參考下
    2022-10-10
  • JS實現(xiàn)選項卡插件的兩種寫法(jQuery和class)

    JS實現(xiàn)選項卡插件的兩種寫法(jQuery和class)

    這篇文章主要為大家詳細介紹了JS實現(xiàn)選項卡插件的兩種寫法:jQuery和class,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2020-12-12
  • JavaScript與DropDownList 區(qū)別分析

    JavaScript與DropDownList 區(qū)別分析

    大家都知道,.NET中一些Web服務(wù)器控件解析并編譯,最終被渲染的時候,其實是轉(zhuǎn)化成了普通的html控件。
    2010-01-01
  • 使用Webpack壓縮與轉(zhuǎn)譯JavaScript代碼的操作方法

    使用Webpack壓縮與轉(zhuǎn)譯JavaScript代碼的操作方法

    在Web開發(fā)中,代碼的性能和加載時間是用戶體驗的重要組成部分,為此,將JavaScript代碼壓縮和優(yōu)化是發(fā)布前一個必不可少的步驟,所以本文給大家介紹了如何使用Webpack壓縮與轉(zhuǎn)譯JavaScript代碼,需要的朋友可以參考下
    2024-05-05
  • innerHTML動態(tài)添加html代碼和腳本兼容多個瀏覽器

    innerHTML動態(tài)添加html代碼和腳本兼容多個瀏覽器

    innerHTML動態(tài)添加html代碼和腳本,給某個元素的innerHTML賦值,并使得值中的js代碼有效且兼容多個瀏覽器,很棒的一個方法
    2014-10-10
  • 使用html2canvas截圖不全問題的解決辦法

    使用html2canvas截圖不全問題的解決辦法

    這篇文章主要給大家介紹了關(guān)于使用html2canvas截圖不全問題的解決辦法,最近在項目中遇到一個需求,需要提供網(wǎng)頁截圖的功能,這里將遇到的問題分享給大家,需要的朋友可以參考下
    2023-07-07
  • 前端JavaScript經(jīng)典之Promise詳解

    前端JavaScript經(jīng)典之Promise詳解

    Promise是為了解決回調(diào)地獄問題而誕生的,它提供了優(yōu)雅的異步回調(diào)解決方案,這篇文章主要介紹了前端JavaScript經(jīng)典之Promise的相關(guān)資料,需要的朋友可以參考下
    2024-09-09
  • 老生常談JavaScript 數(shù)組方法

    老生常談JavaScript 數(shù)組方法

    本文詳細介紹了JavaScript數(shù)組方法的分類和具體用法,涵蓋了數(shù)組的添加/刪除、查找/訪問、遍歷、排序/反轉(zhuǎn)、轉(zhuǎn)換以及一些其他實用方法,每種方法都提供了簡要的解釋和示例代碼,幫助讀者更好地理解和應(yīng)用這些方法,感興趣的朋友一起看看吧
    2025-03-03
  • 詳解js樹形控件—zTree使用總結(jié)

    詳解js樹形控件—zTree使用總結(jié)

    本篇文章主要介紹了js樹形控件—zTree使用總結(jié),樹形控件的使用是應(yīng)用開發(fā)過程中必不可少的。zTree 是一個依靠 jQuery 實現(xiàn)的多功能 “樹插件”。優(yōu)異的性能、靈活的配置、多種功能的組合是 zTree 最大優(yōu)點.
    2016-12-12
  • 微信小程序 實例開發(fā)總結(jié)

    微信小程序 實例開發(fā)總結(jié)

    這篇文章主要介紹了微信小程序 開發(fā)過程中遇到問題總結(jié)的相關(guān)資料,需要的朋友可以參考下
    2017-04-04

最新評論

定陶县| 甘洛县| 平昌县| 牡丹江市| 岱山县| 赣榆县| 蕲春县| 曲靖市| 新郑市| 墨竹工卡县| 手机| 马尔康县| 越西县| 揭阳市| 中牟县| 罗源县| 隆回县| 砀山县| 恩平市| 星子县| 原阳县| 龙泉市| 株洲县| 兴文县| 蓝田县| 缙云县| 达尔| 句容市| 平果县| 泗水县| 蕲春县| 宾川县| 开鲁县| 星座| 凤城市| 射阳县| 拜泉县| 新巴尔虎右旗| 万州区| 改则县| 修武县|