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

登錄獲取Token和刷新Token的完整實(shí)現(xiàn)流程

 更新時(shí)間:2026年05月09日 08:45:18   作者:北辰alk  
文章介紹了登錄獲取Token和刷新Token的接口及使用流程,包括接口作用、完整流程圖、代碼實(shí)現(xiàn)、關(guān)鍵點(diǎn)總結(jié)和注意事項(xiàng),主要內(nèi)容涉及刷新策略、并發(fā)控制、刷新時(shí)機(jī)、Token失效處理及注意事項(xiàng),可幫助開發(fā)者實(shí)現(xiàn)Token的獲取和自動(dòng)刷新,需要的朋友可以參考下

登錄獲取 Token 和刷新 Token 是兩個(gè)配合使用的接口,下面是完整的使用流程和代碼實(shí)現(xiàn)

一、兩個(gè)接口的作用

接口類型使用時(shí)機(jī)返回內(nèi)容有效期
登錄接口用戶首次登錄accessToken + refreshTokenaccessToken 短期(如30分鐘)
refreshToken 長(zhǎng)期(如7天)
刷新Token接口accessToken 過期時(shí)新的 accessToken + 新的 refreshToken(可選)重新計(jì)時(shí)

二、完整流程圖

用戶登錄
    ↓
調(diào)用登錄接口 → 獲取 accessToken + refreshToken
    ↓
存儲(chǔ)到本地
    ↓
正常請(qǐng)求業(yè)務(wù)接口(攜帶 accessToken)
    ↓
┌─────────────────┐
│ 接口返回401過期?│
└─────────────────┘
    ↓ 是
調(diào)用刷新Token接口(攜帶 refreshToken)
    ↓
獲取新的 accessToken
    ↓
更新本地存儲(chǔ)
    ↓
重試剛才失敗的請(qǐng)求
    ↓
繼續(xù)正常請(qǐng)求...

三、完整代碼實(shí)現(xiàn)

1. 定義 API 接口

// api/auth.js
const BASE_URL = 'https://your-api.com';

export default {
    // 登錄接口
    login(data) {
        return uni.request({
            url: `${BASE_URL}/login`,
            method: 'POST',
            data: data
        });
    },
    
    // 刷新Token接口
    refreshToken(refreshToken) {
        return uni.request({
            url: `${BASE_URL}/refresh`,
            method: 'POST',
            data: { refreshToken: refreshToken }
        });
    }
};

2. 登錄邏輯

// pages/login/login.vue
export default {
    data() {
        return {
            username: '',
            password: ''
        }
    },
    methods: {
        async handleLogin() {
            try {
                // 1. 調(diào)用登錄接口
                const res = await this.$api.login({
                    username: this.username,
                    password: this.password
                });
                
                if (res.code === 0) {
                    const { accessToken, refreshToken, expiresIn, tokenType } = res.data;
                    
                    // 2. 存儲(chǔ)Token
                    this.saveToken({
                        accessToken,
                        refreshToken,
                        expiresIn,  // 有效期時(shí)長(zhǎng)(毫秒)
                        tokenType   // Bearer 或 null
                    });
                    
                    // 3. 跳轉(zhuǎn)到首頁
                    uni.switchTab({
                        url: '/pages/index/index'
                    });
                } else {
                    uni.showToast({
                        title: res.msg || '登錄失敗',
                        icon: 'none'
                    });
                }
            } catch (error) {
                console.error('登錄失敗', error);
            }
        },
        
        // 存儲(chǔ)Token
        saveToken(tokenData) {
            const now = Date.now();
            
            // 存儲(chǔ)基礎(chǔ)信息
            uni.setStorageSync('accessToken', tokenData.accessToken);
            uni.setStorageSync('refreshToken', tokenData.refreshToken);
            uni.setStorageSync('tokenType', tokenData.tokenType || '');
            
            // 計(jì)算過期時(shí)間(絕對(duì)時(shí)間戳)
            if (tokenData.expiresIn) {
                uni.setStorageSync('expiresTime', now + tokenData.expiresIn);
            } else if (tokenData.expiresTime) {
                uni.setStorageSync('expiresTime', tokenData.expiresTime);
            }
        }
    }
};

3. 刷新Token邏輯

// utils/refreshToken.js

// 防止多個(gè)請(qǐng)求同時(shí)刷新Token(鎖機(jī)制)
let isRefreshing = false;
let refreshSubscribers = [];

// 待刷新期間緩存的請(qǐng)求
function subscribeTokenRefresh(callback) {
    refreshSubscribers.push(callback);
}

function onTokenRefreshed(newToken) {
    refreshSubscribers.forEach(callback => callback(newToken));
    refreshSubscribers = [];
}

// 刷新Token
async function refreshAccessToken() {
    try {
        const refreshToken = uni.getStorageSync('refreshToken');
        
        if (!refreshToken) {
            // 沒有refreshToken,跳轉(zhuǎn)登錄
            redirectToLogin();
            return null;
        }
        
        // 調(diào)用刷新接口
        const res = await uni.request({
            url: 'https://your-api.com/refresh',
            method: 'POST',
            data: { refreshToken: refreshToken }
        });
        
        if (res.statusCode === 200 && res.data.code === 0) {
            const { accessToken, refreshToken: newRefreshToken, expiresIn } = res.data.data;
            
            // 更新存儲(chǔ)
            uni.setStorageSync('accessToken', accessToken);
            uni.setStorageSync('expiresTime', Date.now() + expiresIn);
            
            // 如果返回了新的refreshToken(通常刷新接口也會(huì)返回新的)
            if (newRefreshToken) {
                uni.setStorageSync('refreshToken', newRefreshToken);
            }
            
            // 通知所有等待的請(qǐng)求
            onTokenRefreshed(accessToken);
            
            return accessToken;
        } else {
            // 刷新失敗,清空Token并跳轉(zhuǎn)登錄
            clearTokenAndRedirect();
            return null;
        }
    } catch (error) {
        console.error('刷新Token失敗', error);
        clearTokenAndRedirect();
        return null;
    } finally {
        isRefreshing = false;
    }
}

// 跳轉(zhuǎn)登錄頁
function redirectToLogin() {
    clearToken();
    uni.reLaunch({
        url: '/pages/login/login'
    });
}

// 清空Token
function clearToken() {
    uni.removeStorageSync('accessToken');
    uni.removeStorageSync('refreshToken');
    uni.removeStorageSync('expiresTime');
    uni.removeStorageSync('tokenType');
}

// 導(dǎo)出刷新方法
export { refreshAccessToken, clearToken, redirectToLogin };

4. 請(qǐng)求攔截器(自動(dòng)處理Token過期)

// utils/request.js
import { refreshAccessToken, redirectToLogin } from './refreshToken.js';

// 請(qǐng)求隊(duì)列(存放過期期間等待的請(qǐng)求)
let requestQueue = [];
let isRefreshing = false;

// 發(fā)起請(qǐng)求的核心方法
function request(config) {
    return new Promise((resolve, reject) => {
        // 1. 檢查Token是否過期
        const expiresTime = uni.getStorageSync('expiresTime');
        const now = Date.now();
        
        // 判斷是否需要刷新Token
        if (expiresTime && now >= expiresTime) {
            // Token已過期,需要刷新
            if (!isRefreshing) {
                isRefreshing = true;
                
                // 刷新Token
                refreshAccessToken().then(newToken => {
                    if (newToken) {
                        // 刷新成功,執(zhí)行隊(duì)列中的請(qǐng)求
                        requestQueue.forEach(cb => cb(newToken));
                        requestQueue = [];
                        // 重新發(fā)起當(dāng)前請(qǐng)求
                        doRequest(config).then(resolve).catch(reject);
                    } else {
                        reject({ message: 'Token刷新失敗' });
                    }
                    isRefreshing = false;
                }).catch(() => {
                    isRefreshing = false;
                    reject({ message: 'Token刷新失敗' });
                });
            }
            
            // 將當(dāng)前請(qǐng)求加入隊(duì)列,等刷新完成后重試
            requestQueue.push((newToken) => {
                // 更新config中的token
                config.header = getAuthHeader(newToken);
                doRequest(config).then(resolve).catch(reject);
            });
        } else {
            // Token未過期,直接發(fā)起請(qǐng)求
            config.header = getAuthHeader();
            doRequest(config).then(resolve).catch(reject);
        }
    });
}

// 實(shí)際發(fā)起請(qǐng)求
function doRequest(config) {
    return new Promise((resolve, reject) => {
        uni.request({
            url: config.url,
            method: config.method || 'GET',
            data: config.data,
            header: config.header,
            success: (res) => {
                // 處理業(yè)務(wù)錯(cuò)誤(如Token無效)
                if (res.data && res.data.code === 401) {
                    // Token無效,強(qiáng)制刷新
                    refreshAccessToken().then(() => {
                        // 重試請(qǐng)求
                        request(config).then(resolve).catch(reject);
                    }).catch(() => {
                        redirectToLogin();
                        reject({ message: '登錄已過期' });
                    });
                } else {
                    resolve(res);
                }
            },
            fail: (err) => {
                reject(err);
            }
        });
    });
}

// 獲取認(rèn)證請(qǐng)求頭
function getAuthHeader(customToken = null) {
    const token = customToken || uni.getStorageSync('accessToken');
    const tokenType = uni.getStorageSync('tokenType');
    
    if (tokenType === 'Bearer') {
        return { 'Authorization': `Bearer ${token}` };
    } else {
        return { 'accessToken': token };
    }
}

export default request;

5. 實(shí)際使用示例

// 在 main.js 中全局掛載
import request from '@/utils/request.js';
Vue.prototype.$request = request;

// 在頁面中使用
// pages/index/index.vue
export default {
    async mounted() {
        try {
            // 自動(dòng)處理Token過期和刷新
            const res = await this.$request({
                url: 'https://your-api.com/user/info',
                method: 'GET'
            });
            
            console.log('用戶信息:', res.data);
        } catch (error) {
            console.error('請(qǐng)求失敗:', error);
            if (error.message === '登錄已過期') {
                // 跳轉(zhuǎn)到登錄頁
                uni.navigateTo({
                    url: '/pages/login/login'
                });
            }
        }
    }
};

四、關(guān)鍵點(diǎn)總結(jié)

1. 刷新策略

  • 主動(dòng)刷新:每次請(qǐng)求前檢查,剩余時(shí)間 < 5分鐘就提前刷新
  • 被動(dòng)刷新:請(qǐng)求返回401時(shí)觸發(fā)刷新

2. 并發(fā)控制(重要)

// 避免短時(shí)間內(nèi)多次刷新Token
let isRefreshing = false;  // 鎖標(biāo)志
let requestQueue = [];      // 等待隊(duì)列

3. 刷新時(shí)機(jī)

// 在請(qǐng)求攔截器中判斷
const expiresTime = uni.getStorageSync('expiresTime');
const timeRemaining = expiresTime - Date.now();

if (timeRemaining < 5 * 60 * 1000) {  // 剩余不足5分鐘
    await refreshAccessToken();  // 提前刷新
}

4. Token失效后處理

// 刷新接口也返回401時(shí),清空所有Token并跳轉(zhuǎn)登錄
if (res.statusCode === 401) {
    uni.removeStorageSync('accessToken');
    uni.removeStorageSync('refreshToken');
    uni.reLaunch({ url: '/pages/login/login' });
}

五、注意事項(xiàng)

  1. refreshToken 也有有效期,通常比 accessToken 長(zhǎng)(如7天、30天),過期后需要重新登錄
  2. 刷新接口也要做防抖,避免短時(shí)間內(nèi)多次調(diào)用
  3. 存儲(chǔ)到本地時(shí)建議加密(特別是 refreshToken)
  4. 登出時(shí)要同時(shí)清空 accessToken 和 refreshToken
  5. 刷新Token接口建議使用 POST 請(qǐng)求,并將 refreshToken 放在 Body 中

按照以上方案實(shí)現(xiàn),就能完美處理 Token 的獲取和自動(dòng)刷新了。

以上就是登錄獲取Token和刷新Token的完整實(shí)現(xiàn)流程的詳細(xì)內(nèi)容,更多關(guān)于登錄獲取Token和刷新Token的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • JavaScript實(shí)現(xiàn)打字游戲

    JavaScript實(shí)現(xiàn)打字游戲

    這篇文章主要為大家詳細(xì)介紹了JavaScript實(shí)現(xiàn)打字游戲,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-02-02
  • 小程序?qū)崿F(xiàn)留言板

    小程序?qū)崿F(xiàn)留言板

    這篇文章主要為大家詳細(xì)介紹了微信小程序?qū)崿F(xiàn)留言板,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2018-11-11
  • JS 對(duì)象(Object)和字符串(String)互轉(zhuǎn)方法

    JS 對(duì)象(Object)和字符串(String)互轉(zhuǎn)方法

    下面小編就為大家?guī)硪黄狫S 對(duì)象(Object)和字符串(String)互轉(zhuǎn)方法。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2016-05-05
  • JS實(shí)現(xiàn)元素上下左右移動(dòng)效果

    JS實(shí)現(xiàn)元素上下左右移動(dòng)效果

    這篇文章主要為大家詳細(xì)介紹了JS實(shí)現(xiàn)元素上下左右移動(dòng)效果,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-10-10
  • javascript中toFixed()四舍五入使用方法詳解

    javascript中toFixed()四舍五入使用方法詳解

    最近做的項(xiàng)目涉及到金額的計(jì)算,有一種方式就是進(jìn)行四舍五入的規(guī)則進(jìn)行小數(shù)點(diǎn)后面的尾數(shù)處理,以前一直以為toFixed方法就是四舍五入的,這里為大家分享一下
    2018-09-09
  • js?window.addEventListener?簡(jiǎn)介

    js?window.addEventListener?簡(jiǎn)介

    window.addEventListener是JavaScript中用于添加事件監(jiān)聽器的方法,允許在發(fā)生特定事件時(shí)執(zhí)行函數(shù),本文給大家介紹js?window.addEventListener?是什么,感興趣的朋友一起看看吧
    2024-09-09
  • js圖片輪播手動(dòng)切換特效

    js圖片輪播手動(dòng)切換特效

    這篇文章主要為大家詳細(xì)介紹了js圖片輪播手動(dòng)切換特效,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-01-01
  • 理解Javascript_09_Function與Object

    理解Javascript_09_Function與Object

    在《理解Javascript_08_函數(shù)對(duì)象》中講解了很多函數(shù)對(duì)象的問題,同時(shí)也留下了許多疑問,今天讓我們來解答部分問題。
    2010-10-10
  • js實(shí)現(xiàn)仿百度風(fēng)云榜可重復(fù)多次調(diào)用的TAB切換選項(xiàng)卡效果

    js實(shí)現(xiàn)仿百度風(fēng)云榜可重復(fù)多次調(diào)用的TAB切換選項(xiàng)卡效果

    這篇文章主要介紹了js實(shí)現(xiàn)仿百度風(fēng)云榜可重復(fù)多次調(diào)用的TAB切換選項(xiàng)卡效果,涉及javascript鼠標(biāo)事件及頁面元素遍歷調(diào)用的實(shí)現(xiàn)技巧,非常簡(jiǎn)單實(shí)用,需要的朋友可以參考下
    2015-08-08
  • 前端JavaScript異步處理全方案詳解

    前端JavaScript異步處理全方案詳解

    JavaScript 的單線程特性決定了它必須依靠異步機(jī)制來處理耗時(shí)操作,本文會(huì)系統(tǒng)梳理 JS 處理異步的所有主流方案,并通過示例、對(duì)比和場(chǎng)景分析,幫助你在實(shí)際開發(fā)中做出合理選擇
    2026-05-05

最新評(píng)論

靖远县| 临颍县| 尖扎县| 宝丰县| 达孜县| 绥阳县| 察隅县| 西乌珠穆沁旗| 伊金霍洛旗| 阿尔山市| 佛学| 井陉县| 蚌埠市| 富裕县| 雅安市| 当雄县| 长宁县| 湖北省| 栖霞市| 阜阳市| 札达县| 兴宁市| 河间市| 新干县| 虞城县| 安福县| 历史| 崇明县| 凤翔县| 安化县| 红原县| 太保市| 耒阳市| 晋江市| 洪洞县| 滨海县| 玛曲县| 方山县| 集贤县| 海南省| 秦皇岛市|