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

深度拆解前端雙Token無感刷新的實(shí)現(xiàn)機(jī)制

 更新時(shí)間:2026年03月17日 09:02:32   作者:發(fā)現(xiàn)一只大呆瓜  
在前后端分離的項(xiàng)目中,為了安全,Token?通常會(huì)設(shè)置有效期,本文將深度拆解?“雙?Token?無感刷新”?的實(shí)現(xiàn)機(jī)制,有需要的小伙伴可以跟隨小編一起學(xué)習(xí)一下

前言

在前后端分離的項(xiàng)目中,為了安全,Token 通常會(huì)設(shè)置有效期。但如果 Token 過期時(shí)強(qiáng)制用戶重新登錄,會(huì)極大地破壞用戶體驗(yàn)。如何做到在用戶毫無察覺的情況下,自動(dòng)完成 Token 的續(xù)期?本文將深度拆解 “雙 Token 無感刷新” 的實(shí)現(xiàn)機(jī)制。

一、 為什么需要“無感刷新”?

舉個(gè)簡單例子,你正在某 App 編輯內(nèi)容,中途切出幾分鐘,再切回來時(shí),直接彈出登錄頁,提示“登錄已過期,請重新登錄”,這種場景很容易讓用戶流失。

傳統(tǒng)的單 Token 方案存在一個(gè)兩難境地:

  • 有效期過短:用戶操作頻繁,動(dòng)不動(dòng)就跳回登錄頁,用戶體驗(yàn)極差。
  • 有效期過長:Token 一旦被截獲,風(fēng)險(xiǎn)極高。

解決方案:雙 Token 機(jī)制

  • access_token:訪問令牌。有效期短(如 1 小時(shí)),每次接口請求都攜帶,降低泄露風(fēng)險(xiǎn)。
  • refresh_token:刷新令牌。有效期長(如 7 天),僅用于 access_token 過期時(shí)換取新令牌。

只要用戶在 7 天內(nèi)活躍過,系統(tǒng)就能通過 refresh_token 自動(dòng)“續(xù)命”,實(shí)現(xiàn)長效無感登錄。

二、 核心流程設(shè)計(jì)

正常請求:前端攜帶 access_token 訪問。

觸發(fā)過期:后端返回 401 Unauthorized。

判斷邏輯

  • 如果是普通接口報(bào) 401:說明 access_token 失效,嘗試刷新。
  • 如果是刷新接口報(bào) 401:說明 refresh_token 也失效了,強(qiáng)制重新登錄。

無感替換:前端自動(dòng)調(diào)用刷新接口,獲取新 Token 覆蓋本地存儲(chǔ),并重新發(fā)起之前失敗的請求。

三、 細(xì)節(jié)攻堅(jiān):如何處理并發(fā)請求?

痛點(diǎn):如果頁面同時(shí)發(fā)出了 5 個(gè)請求,而此時(shí) Token 剛好過期,會(huì)導(dǎo)致這 5 個(gè)請求同時(shí)觸發(fā)“刷新 Token”的操作,造成資源浪費(fèi)甚至后端異常。

解決策略

  • 狀態(tài)鎖 (refreshing) :記錄當(dāng)前是否正在刷新中。
  • 任務(wù)隊(duì)列 (queue) :在刷新期間到達(dá)的請求,先暫存起來,不直接報(bào)錯(cuò)。
  • 批量回放:等待 Token 刷新成功后,依次執(zhí)行隊(duì)列里的請求,實(shí)現(xiàn)“無感”銜接。

四、 代碼實(shí)現(xiàn) (Axios 攔截器)

以下是基于 Axios 的完整工程化實(shí)現(xiàn):

import axios, { AxiosRequestConfig } from 'axios';

interface PendingTask {
    config: AxiosRequestConfig;
    resolve: Function;
}

let refreshing = false; // 狀態(tài)鎖:標(biāo)志是否正在刷新 Token
let queue: PendingTask[] = []; // 請求隊(duì)列:暫存 Token 刷新期間的請求

const axiosInstance = axios.create({
    baseURL: '/api'
});

// 1. 請求攔截器:自動(dòng)注入 Token
axiosInstance.interceptors.request.use((config) => {
    const accessToken = localStorage.getItem('access_token');
    if (accessToken && config.headers) {
        config.headers.authorization = `Bearer ${accessToken}`;
    }
    return config;
});

// 2. 響應(yīng)攔截器:處理 Token 過期
axiosInstance.interceptors.response.use(
    (response) => response,
    async (error) => {
        const { data, config } = error.response;

        // 情況 A:正在刷新 Token 中,將后續(xù)請求存入隊(duì)列
        if (refreshing) {
            return new Promise((resolve) => {
                queue.push({ config, resolve });
            });
        }

        // 情況 B:access_token 過期 (狀態(tài)碼 401 且非刷新接口本身)
        if (data.statusCode === 401 && !config.url.includes('/refresh')) {
            refreshing = true;
            
            try {
                const res = await refreshToken();
                refreshing = false;

                if (res.status === 200) {
                    // 核心邏輯:Token 刷新成功,回放隊(duì)列中的所有請求
                    queue.forEach(({ config, resolve }) => {
                        resolve(axiosInstance(config));
                    });
                    queue = []; // 清空隊(duì)列
                    
                    // 執(zhí)行當(dāng)前觸發(fā)刷新的那個(gè)請求
                    return axiosInstance(config);
                }
            } catch (err) {
                refreshing = false;
                queue = [];
                // 情況 C:refresh_token 也過期了,徹底清除登錄態(tài)
                localStorage.clear();
                window.location.href = '/login';
                return Promise.reject(err);
            }
        }

        return Promise.reject(error);
    }
);

/**
 * 刷新 Token 的異步方法
 */
async function refreshToken() {
    const res = await axios.get('/api/refresh', {
        params: {
            token: localStorage.getItem('refresh_token')
        }
    });
    // 更新本地存儲(chǔ)
    localStorage.setItem('access_token', res.data.accessToken);
    localStorage.setItem('refresh_token', res.data.refreshToken);
    return res;
}

五、 注意事項(xiàng)

  • 并發(fā)請求的 Promise 掛起:在 refreshingtrue 時(shí),返回一個(gè)不帶 resolvenew Promise 是關(guān)鍵,它能讓 Axios 請求處于 pending 狀態(tài)。
  • 錯(cuò)誤捕獲refreshToken 接口本身報(bào)錯(cuò)(如 500 或 401)必須妥善處理,直接引導(dǎo)至登錄頁。
  • 安全性:普通項(xiàng)目中可以使用 localStorage,但在更高要求的項(xiàng)目中,建議配合 HttpOnly Cookie 存儲(chǔ) refresh_token 以防 XSS 攻擊。
  • 接口重定向陷阱:確保刷新 Token 的接口不會(huì)再次進(jìn)入 401 攔截死循環(huán)。

到此這篇關(guān)于深度拆解前端雙Token無感刷新的實(shí)現(xiàn)機(jī)制的文章就介紹到這了,更多相關(guān)前端無感刷新token內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Echarts中l(wèi)egend屬性使用的方法詳解

    Echarts中l(wèi)egend屬性使用的方法詳解

    Echarts可以幫助我們快速構(gòu)建柱狀圖、餅圖、條形圖,這對于多圖形化展示數(shù)據(jù)來說尤其方便,可幫助我們快速開發(fā),下面這篇文章主要給大家介紹了關(guān)于Echarts中l(wèi)egend屬性使用的相關(guān)資料,需要的朋友可以參考下
    2022-04-04
  • 深入了解TypeScript中的映射類型

    深入了解TypeScript中的映射類型

    在?TypeScript?中,映射類型可以幫助我們避免編寫重復(fù)的代碼,它可以根據(jù)現(xiàn)有類型和定義的一些規(guī)則來創(chuàng)建新類型。本文我們就來看一下什么是映射類型以及如何構(gòu)建自己的映射類型
    2022-08-08
  • javascript:文字不間斷向左移動(dòng)的實(shí)例代碼

    javascript:文字不間斷向左移動(dòng)的實(shí)例代碼

    這篇文章介紹了javascript:文字不間斷向左移動(dòng)的實(shí)例代碼,有需要的朋友可以參考一下
    2013-08-08
  • JavaScript中工廠函數(shù)與構(gòu)造函數(shù)示例詳解

    JavaScript中工廠函數(shù)與構(gòu)造函數(shù)示例詳解

    這篇文章主要給大家介紹了關(guān)于JavaScript中工廠函數(shù)與構(gòu)造函數(shù)的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家學(xué)習(xí)或者使用JavaScript具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-05-05
  • JavaScript學(xué)習(xí)小結(jié)(7)之JS RegExp

    JavaScript學(xué)習(xí)小結(jié)(7)之JS RegExp

    在js中,正則表達(dá)式是由一個(gè)RegExp對象表示的,RegExp 是正則表達(dá)式的縮寫。RegExp簡單的模式可以是一個(gè)單獨(dú)的字符。更復(fù)雜的模式包括了更多的字符,并可用于解析、格式檢查、替換等等??梢允褂靡粋€(gè)RegExp()構(gòu)造函數(shù)來創(chuàng)建RegExp對象,也可以使用直接量語法
    2015-11-11
  • JavaScript如何判斷一個(gè)對象是空對象(附5種常見方法)

    JavaScript如何判斷一個(gè)對象是空對象(附5種常見方法)

    在JavaScript中判斷對象的類型是開發(fā)過程中的一個(gè)常見需求,尤其是在處理不確定類型的數(shù)據(jù)時(shí),這篇文章主要介紹了JavaScript如何判斷一個(gè)對象是空對象的相關(guān)資料,需要的朋友可以參考下
    2025-04-04
  • 關(guān)于layui時(shí)間回顯問題的解決方法

    關(guān)于layui時(shí)間回顯問題的解決方法

    今天小編就為大家分享一篇關(guān)于layui時(shí)間回顯問題的解決方法,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2019-09-09
  • JavaScript實(shí)現(xiàn)網(wǎng)頁動(dòng)態(tài)生成表格

    JavaScript實(shí)現(xiàn)網(wǎng)頁動(dòng)態(tài)生成表格

    這篇文章主要為大家詳細(xì)介紹了JavaScript實(shí)現(xiàn)網(wǎng)頁動(dòng)態(tài)生成表格,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2020-11-11
  • js導(dǎo)出txt示例代碼

    js導(dǎo)出txt示例代碼

    很多新手朋友們都不知道js怎么導(dǎo)出txt,下面有個(gè)不錯(cuò)的示例,大家可以參考下
    2014-01-01
  • 微信小程序?qū)崿F(xiàn)同時(shí)上傳多張圖片

    微信小程序?qū)崿F(xiàn)同時(shí)上傳多張圖片

    這篇文章主要為大家詳細(xì)介紹了微信小程序?qū)崿F(xiàn)同時(shí)上傳多張圖片,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2020-02-02

最新評(píng)論

青铜峡市| 桐梓县| 剑阁县| 临湘市| 定陶县| 三明市| 长丰县| 九龙坡区| 高阳县| 青川县| 邵阳县| 马公市| 石家庄市| 武宁县| 贡觉县| 石楼县| 丰台区| 正阳县| 义马市| 获嘉县| 绍兴市| 罗平县| 石阡县| 内江市| 柘荣县| 陇南市| 兴仁县| 南昌市| 健康| 定日县| 栾川县| 颍上县| 安西县| 耒阳市| 涿州市| 越西县| 南靖县| 高雄县| 临沂市| 江口县| 鄂托克前旗|