第一次接觸token處理,初來乍到,說錯的地方還請各位多多指教。
token身份驗證機制
客戶端登錄請求成功后,服務器將用戶信息(如用戶id)使用特殊算法加密后作為驗證的標志發送給用戶(即token),當用戶下次發起請求時,會將這個token捎帶過來,服務器再將這個token通過解密后進行驗證,通過的話,則向客戶端返回請求的數據;反之,則請求失敗。
token優點
它是無狀態的,且服務器不用像傳統的身份認證(session)那樣需要保存會話信息,減輕了服務器的壓力。
vue的token刷新處理
在對token身份驗證機制進行一次簡單介紹后,進入正文...
一般為了安全性,token都會設置一個過期時間,在過期之后就無法請求相關接口了,這時應該怎么辦呢,是直接退出登錄嗎?
在目前公司的項目里,為了更好的用戶體驗,我們選擇手動刷新token。登錄請求成功后,會返回一個token和token過期時間,在每次請求api時,前端可以先判斷一下token是否即將過期或已過期,如果是,則請求刷新token的接口,成功替換原來的token之后才可以重新發起請求。
下面,我們直接看代碼,這是在vue的請求攔截器里進行的相關操作:
/*是否有請求正在刷新token*/ window.isRefreshing = false /*被掛起的請求數組*/ let refreshSubscribers = [] /*獲取刷新token請求的token*/ function getRefreshToken () { return JSON.parse(localStorage.auth).refresh_token } /*push所有請求到數組中*/ function subscribeTokenRefresh (cb) { refreshSubscribers.push(cb) } /*刷新請求(refreshSubscribers數組中的請求得到新的token之后會自執行,用新的token去請求數據)*/ function onRrefreshed (token) { refreshSubscribers.map(cb => cb(token)) } /*請求攔截器*/ ajax.interceptors.request.use( config => { const authTmp = localStorage.auth /*判斷是否已登錄*/ if (authTmp) { /*解析登錄信息*/ let auth = JSON.parse(authTmp) /*判斷auth是否存在*/ if (auth) { /*在請求頭中添加token類型、token*/ config.headers.Authorization = auth.token_type + ' ' + auth.token /*判斷刷新token請求的refresh_token是否過期*/ if (util.isRefreshTokenExpired()) { alert('刷新token過期,請重新登錄') /*清除本地保存的auth*/ localStorage.removeItem('auth') window.location.href = '#/login' return } /*判斷token是否將要過期*/ if (util.isTokenExpired() && config.url.indexOf('admin/auth/current') === -1) { /*判斷是否正在刷新*/ if (!window.isRefreshing) { /*將刷新token的標志置為true*/ window.isRefreshing = true /*發起刷新token的請求*/ apiList.refreshToken({refresh_token: getRefreshToken()}).then(res => { /*將標志置為false*/ window.isRefreshing = false /*成功刷新token*/ config.headers.Authorization = res.data.data.token_type + ' ' + res.data.data.token /*更新auth*/ localStorage.setItem('auth', JSON.stringify(res.data.data)) /*執行數組里的函數,重新發起被掛起的請求*/ onRrefreshed(res.data.data.token) /*執行onRefreshed函數后清空數組中保存的請求*/ refreshSubscribers = [] }).catch(err => { alert(err.response.data.message) /*清除本地保存的auth*/ // localStorage.removeItem('auth') window.location.href = '#/login' }) } /*把請求(token)=>{....}都push到一個數組中*/ let retry = new Promise((resolve, reject) => { /*(token) => {...}這個函數就是回調函數*/ subscribeTokenRefresh((token) => { config.headers.Authorization = 'Bearer ' + token /*將請求掛起*/ resolve(config) }) }) return retry } } return config } else { /*未登錄直接返回配置信息*/ return config } }, /*錯誤操作*/ err => { return Promise.reject(err) } )
這里需要注意幾點:
1、當token即將過期或者已過期時,原則上,我們只需要有一個接口去觸發刷新token的請求即可,這里的isRefreshing 變量,就起到這樣一個監控的作用,它相當于一把鎖,當刷新token的操作被觸發后,其他的觸發操作就被排斥在外了。
window.isRefreshing = false
2、刷新token的接口,用到了一個另外的token(refresh_token),這也是出于安全性考慮的,并且它也有過期時間,不過這個過期時間一般都比普通token的過期時間要長,所以在上面代碼中,會發現,我在請求攔截中優先判斷了refresh_token是否過期,如果過期則直接退出登錄,不再進行下一步的操作。
/*判斷刷新token請求的refresh_token是否過期*/ if (util.isRefreshTokenExpired() && config.url.indexOf('admin/auth/current') === -1) { alert('刷新token過期,請重新登錄') /*清除本地保存的auth*/ localStorage.removeItem('auth') window.location.href = '#/login' return }
3、在觸發了刷新token的操作后,我們還需要先將其他的請求掛起,在獲取新的token之后再重新發起這些請求。
/*把請求(token)=>{....}都push到一個數組中*/ let retry = new Promise((resolve, reject) => { /*(token) => {...}這個函數就是回調函數*/ subscribeTokenRefresh((token) => { config.headers.Authorization = 'Bearer ' + token /*將請求掛起*/ resolve(config) }) }) return retry
在刷新token請求的成功回調里執行下面代碼,重新發起請求。
/*執行數組里的函數,重新發起被掛起的請求*/ onRrefreshed(res.data.data.token)
4、因為有人在評論里問util文件,應該是想知道具體怎么判斷token過期的,其實在獲得token時,是有返回一個token過期時間 ,你可以先將它先保存起來,然后在需要時,拿出來與本地時間比較即可
/*判斷token是否過期*/ function isTokenExpired() { /*從localStorage中取出token過期時間*/ let expiredTime = new Date(JSON.parse(localStorage.auth).expired_at).getTime() / 1000 /*獲取本地時間*/ let nowTime = new Date().getTime() / 1000 /*獲取校驗時間差*/ let diffTime = JSON.parse(sessionStorage.diffTime) /*校驗本地時間*/ nowTime -= diffTime /*如果 < 10分鐘,則說明即將過期*/ return (expiredTime - nowTime) < 10*60 }
聲明:本網頁內容旨在傳播知識,若有侵權等問題請及時與本網聯系,我們將在第一時間刪除處理。TEL:177 7030 7066 E-MAIL:11247931@qq.com