在vue項目中實現(xiàn)Token替換和請求攔截

? ? ? ? Token在計算機身份認證中是令牌(臨時)的意思,在詞法分析中是標記的意思。一般作為邀請、登錄系統(tǒng)使用。

????????在vue項目中實現(xiàn)Token替換----前端學(xué)海

????????這里使用的vue中的請求攔截器,根據(jù)后端返回的Token的生成時間,以及該Token的有效期和當(dāng)前發(fā)送請求的時間進行計算,判斷當(dāng)前Token是否快要過期,以確定是否開始請求新的Token。

計算公式為:Token有效時間 -(當(dāng)前時間 - Token的生成時間)= 剩余的Token有效時間

? ??????這里我的判斷為,Token剩余有效時間,小于五分鐘的時候,調(diào)用刷新Token的接口,請求最新的Token。

在請求新Token期間,可能同時會有好幾個請求同時發(fā)起,我們在這里將這些請求攔截下來,通過Promise,將這些請求,完整的防在待執(zhí)行的請求隊列中,

當(dāng)新的Token請求成功,更新本地的Token之后,開始執(zhí)行攔截的請求隊列,并將這些請求的header中的Token替換為最新的Token,完成Token整個流程。

如果請求新Token接口失敗,并且原Token已過了有效期時,提示登錄過期,重定向到登錄頁,重新登錄!

????????如有建議,歡迎在評論區(qū)指出?。?!

請看代碼

import axios from 'axios' // 引入axios

import loginApi from './api/article/loginApi'

import router from '../router'

import qs from 'qs'

import {

? Message

} from 'element-ui'

console.info(location.hostname)

if (location.hostname === 'localhost' ||

? location.hostname === '127.0.0.1') {

? axios.defaults.baseURL = '/api/yifd'

} else {

? axios.defaults.baseURL = `https://${location.hostname}/proxy`

}

// eslint-disable-next-line no-unused-vars

/* 是否正在刷新的標志 */

window.isRefreshing = false

// token是否過期,默認為否

window.tokenOverdue = false

/* 存儲請求的數(shù)組 */

const refreshSubscribers = []

/* 將所有的請求都push到數(shù)組中,其實數(shù)組是[function(token){}, function(token){},...] */

function subscribeTokenRefresh (cb) {

? refreshSubscribers.push(cb)

}

/* 數(shù)組中的請求得到新的token之后自執(zhí)行,用新的token去請求數(shù)據(jù) */

function onRrefreshed (token) {

? refreshSubscribers.map(cb => cb(token))

}

function tokenIsOverdue () { // token是否過期

? const time = window.sessionStorage.getItem('tokenTime') // token有效期,秒級,后端返回,瀏覽器存儲的

? const queryTime = parseInt(window.sessionStorage.getItem('tokenQueryTime')) // token獲取的時間,秒級

? const nowTime = new Date().getTime()

? const arealyTime = Math.ceil(nowTime / 1000) - queryTime // 已經(jīng)過去的時間

? // console.log(time, queryTime, Math.ceil(nowTime / 1000), arealyTime)

? if ((time - arealyTime) < 300) { // token五分鐘內(nèi)即將過期時開啟替換token,目前后臺的token有效期為30分鐘。

? ? return true

? } else {

? ? return false

? }

}

tokenIsOverdue()

/**

* 請求失敗后的錯誤統(tǒng)一處理

* @param {Number} status 請求失敗的狀態(tài)碼

*/

const errorHandle = (response) => {

? const status = response.data.code

? const msg = response.data.msg

? // 狀態(tài)碼判斷

? switch (status) {

? ? // 401:未登錄狀態(tài)或token過期,跳轉(zhuǎn)登錄頁

? ? case 401:

? ? ? Message.error(msg)

? ? ? break

? ? case 403:

? ? ? alert(msg)

? ? ? break

? ? ? // 404:請求不存在

? ? case 404:

? ? ? router.push({

? ? ? ? path: '/404'

? ? ? })

? ? ? Message.error('請求的資源不在')

? ? ? break

? ? ? // 服務(wù)器錯誤

? ? case 500:

? ? ? Message.error(msg)

? ? ? break

? ? default:

? ? ? console.log(msg)

? }

}

// 創(chuàng)建axios實例

var instance = axios.create({

? timeout: 1000 * 100

})

/**

* 請求攔截器

* 每次請求前,如果存在token則在請求頭中攜帶token

*/

instance.interceptors.request.use(

? request => {

? ? // // 該位置會獲取登陸成功時的token數(shù)據(jù)

? ? const token = window.sessionStorage.getItem('token')

? ? /* 判斷用戶是否已經(jīng)登錄 */

? ? if (token) {

? ? ? /* 請求頭添加token信息 */

? ? ? request.headers.Authorization = token

? ? ? if (request.url.includes('ossUpload')) {

? ? ? ? request.timeout = 600000

? ? ? } else {

? ? ? ? request.timeout = 100000

? ? ? }

? ? ? /* 判斷token是否即將過期 */

? ? ? /*? `oauth/token`是刷新token的接口,只有當(dāng)token將要過期且不是請求刷新token的接口才會進入 */

? ? ? if (tokenIsOverdue() && !request.url.includes('oauth/token')) {

? ? ? ? /* 首先所有的請求來了,我們要先判斷當(dāng)前是否正在刷新,如果不是,將刷新的標志置為true并請求刷新token;如果是,將請求存儲到數(shù)組中 */

? ? ? ? if (!window.isRefreshing) {

? ? ? ? ? window.isRefreshing = true

? ? ? ? ? loginApi.apiLogin(

? ? ? ? ? ? qs.stringify({

? ? ? ? ? ? ? grant_type: 'refresh_token',

? ? ? ? ? ? ? refresh_token: window.sessionStorage.getItem('refresh'),

? ? ? ? ? ? ? client_id: 'trade',

? ? ? ? ? ? ? client_secret: 'homedone'

? ? ? ? ? ? })

? ? ? ? ? ).then(res => {

? ? ? ? ? ? if (res.data.code === 200) {

? ? ? ? ? ? ? /* 將刷新的token替代老的token */

? ? ? ? ? ? ? request.headers.Authorization = 'Bearer ' + res.data.data.access_token

? ? ? ? ? ? ? /* 更新內(nèi)存的token信息 */

? ? ? ? ? ? ? const resData = res.data.data

? ? ? ? ? ? ? window.sessionStorage.setItem('token', 'Bearer' + ' ' + resData.access_token) // 存入token Bearer

? ? ? ? ? ? ? window.sessionStorage.setItem('refresh', resData.refresh_token) // 存入刷新token,用來替換過期token

? ? ? ? ? ? ? window.sessionStorage.setItem('tokenTime', res.data.data.expires_in) // token有效期,秒級

? ? ? ? ? ? ? window.sessionStorage.setItem('tokenQueryTime', Math.floor(res.data.timestamp / 1000)) // token獲取時間,秒級

? ? ? ? ? ? ? /* 執(zhí)行數(shù)組里的請求,重新發(fā)起被掛起的請求 */

? ? ? ? ? ? ? onRrefreshed(res.data.data.access_token)

? ? ? ? ? ? } else { // 目前做的處理,當(dāng)替換token請求失敗時

? ? ? ? ? ? ? if (res.status === 401) { // 當(dāng)刷新token時為401,標識refresh_token也已經(jīng)過期,需重新登錄

? ? ? ? ? ? ? ? Message.warning('登陸過期請重新登陸!')

? ? ? ? ? ? ? ? window.location = '#/' // 重定向回登錄頁

? ? ? ? ? ? ? ? window.sessionStorage.clear() // 清除包括token的所有緩存,讓用戶重新登錄

? ? ? ? ? ? ? } else {

? ? ? ? ? ? ? ? window.isRefreshing = true

? ? ? ? ? ? ? }

? ? ? ? ? ? }

? ? ? ? ? })

? ? ? ? ? const retry = new Promise((resolve, reject) => {

? ? ? ? ? ? subscribeTokenRefresh((token) => {

? ? ? ? ? ? ? request.headers.Authorization = 'Bearer ' + token

? ? ? ? ? ? ? /* 將請求掛起 */

? ? ? ? ? ? ? resolve(request)

? ? ? ? ? ? })

? ? ? ? ? })

? ? ? ? ? return retry

? ? ? ? }

? ? ? } else {

? ? ? ? return request

? ? ? }

? ? } else {

? ? ? /* 如果沒有登錄直接返回請求 */

? ? ? return request

? ? }

? ? return request

? },

? error => {

? ? return Promise.reject(error)

? }

)

// 響應(yīng)攔截器

instance.interceptors.response.use(

? // 請求成功

? res => {

? ? res.status === 200 ? Promise.resolve(res) : Promise.reject(res)

? ? return res

? },

? // 請求失敗

? error => {

? ? const {

? ? ? response

? ? } = error

? ? if (response) {

? ? ? // 請求已發(fā)出,但是不在2xx的范圍

? ? ? errorHandle(response)

? ? ? return Promise.reject(response)

? ? } else {

? ? ? return Promise.reject(error)

? ? }

? }

)

export default instance

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容