前后端分離中的無痛刷新token機(jī)制

今天我們來說一說前后端分離中的無痛刷新token機(jī)制,在手機(jī)app中應(yīng)該經(jīng)常用到,

大家都知道在前后端是以token的形式交互,既然是token,那么肯定有它的過期時(shí)間,沒有一個(gè)token是永久的,永久的token就相當(dāng)于一串永久的密碼,是不安全的,

那么既然有刷新時(shí)間,問題就來了

前后端交互的過程中token如何存儲(chǔ)?

token過期時(shí),前端該怎么處理

當(dāng)用戶正在操作時(shí),遇到token過期該怎么辦?直接跳回登陸頁面???(你確定這樣用戶不會(huì)打死你嗎,老子好不容易表單填完準(zhǔn)備提交????)

token如何存儲(chǔ)

cookie的大小約4k,兼容性在ie6及以上? ? 都兼容,在瀏覽器和服務(wù)器間來回傳遞,因此它得在服務(wù)器的環(huán)境下運(yùn)行,而且可以設(shè)定過期時(shí)間,默認(rèn)的過期時(shí)間是session會(huì)話結(jié)束。

localStorage的大小約5M,兼容性在ie7及以上都兼容,有瀏覽器就可以,不需要在服務(wù)器的環(huán)境下運(yùn)行, 會(huì)一直存在,除非手動(dòng)清除 。

對于這個(gè)問題,答案大致分為2種

存在cookie中

存在localStorage中

我覺得都可以,兩種我都用??

token過期時(shí),前端該怎么處理

思路:token過期處理方式大概就是:

第一種:跳回登陸頁面重新登陸

第二種:catch401 ,然后重新獲取token

對于第一種,很簡單在vue中我們可以在axios攔截器中這樣寫:

instance.interceptors.response.use(function(response){// 對響應(yīng)數(shù)據(jù)做點(diǎn)什么returnresponse.data},function(error){console.log(error)if(error.response){if(error.response.status===401){Message.error('登陸過期請重新登陸!')setToken('')router.push({name:'login'})}}}// 對響應(yīng)錯(cuò)誤做點(diǎn)什么returnPromise.reject(error.response)})

對于第二種,如何重新獲取token,這就要涉及到后端的知識了

來先讓我講一段廢話??

現(xiàn)代認(rèn)證和/或授權(quán)解決方案已將令牌的概念引入其協(xié)議中。令牌是經(jīng)過特殊處理的數(shù)據(jù),它們可以提供足夠的信息來授權(quán)用戶執(zhí)行操作,或者允許客戶端獲取有關(guān)授權(quán)過程的其他信息(然后完成它)。換句話說,令牌是允許執(zhí)行授權(quán)過程的信息??蛻舳耍ɑ蚴跈?quán)服務(wù)器以外的任何一方)是否可讀取或解析此信息是由實(shí)現(xiàn)定義的。重要的是:客戶端獲取此信息,然后使用它來訪問資源。JSON Web令牌(JWT)規(guī)范定義了一種可以由實(shí)現(xiàn)表示公共令牌信息的方式。

JWT定義了一種方式,其中可以表示與認(rèn)證/授權(quán)過程有關(guān)的某些公共信息。顧名思義,數(shù)據(jù)格式是JSON。JWT具有某些常見字段,例如主題,發(fā)行者,到期時(shí)間等。當(dāng)與其他規(guī)范(如JSON Web簽名(JWS)JSON Web加密(JWE))結(jié)合使用時(shí),JWT變得非常有用。這些規(guī)范不僅提供了授權(quán)令牌通常所需的所有信息,而且還提供了驗(yàn)證令牌內(nèi)容的方法,使其不會(huì)被篡改(JWS)和加密信息以使其保持不透明的方式給客戶(JWE)。數(shù)據(jù)格式的簡單性(及其它優(yōu)點(diǎn))幫助JWT成為最常見的令牌類型之一。如果您有興趣學(xué)習(xí)如何在Web應(yīng)用程序中實(shí)現(xiàn)JWT,請查看Ryan Chenkie的這篇優(yōu)秀文章。

出于本文的目的,我們將關(guān)注兩種最常見的令牌類型:訪問令牌刷新令牌

訪問令牌攜帶必要的信息以直接訪問資源。換句話說,當(dāng)客戶端將訪問令牌傳遞給管理資源的服務(wù)器時(shí),該服務(wù)器可以使用令牌中包含的信息來決定客戶端是否被授權(quán)。訪問令牌通常具有到期日期并且是短暫的。

訪問令牌

刷新令牌包含獲取新訪問令牌所需的信息。換句話說,每當(dāng)訪問令牌需要訪問特定資源時(shí),客戶端可以使用刷新令牌來獲得由認(rèn)證服務(wù)器發(fā)布的新訪問令牌。常見用例包括在舊的訪問令牌過期后獲取新訪問令牌,或者首次訪問新資源。刷新令牌也可以過期,但相當(dāng)長壽。刷新令牌通常受到嚴(yán)格的存儲(chǔ)要求,以確保它們不會(huì)泄露。它們也可以被授權(quán)服務(wù)器列入黑名單。

刷新令牌

標(biāo)記是否不透明通常由實(shí)現(xiàn)定義。通用實(shí)現(xiàn)允許針對訪問令牌進(jìn)行直接授權(quán)檢查。也就是說,當(dāng)訪問令牌被傳遞到管理資源的服務(wù)器時(shí),服務(wù)器可以讀取令牌中包含的信息并自己決定用戶是否被授權(quán)(不需要對授權(quán)服務(wù)器進(jìn)行檢查)。這是必須簽署令牌的原因之一(例如,使用JWS)。另一方面,刷新令牌通常需要檢查授權(quán)服務(wù)器。這種處理授權(quán)檢查的分割方式允許三件事:

改進(jìn)了授權(quán)服務(wù)器的訪問模式(降低負(fù)載,加快檢查速度)

泄漏訪問令牌的訪問窗口較短(這些訪問令牌很快過期,減少了泄露令牌允許訪問受保護(hù)資源的機(jī)會(huì))

滑動(dòng)會(huì)話(見下文)

滑動(dòng)會(huì)話是在一段時(shí)間不活動(dòng)后到期的會(huì)話??梢韵胂?,使用訪問令牌和刷新令牌可以輕松實(shí)現(xiàn)。當(dāng)用戶執(zhí)行操作時(shí),將發(fā)出新的訪問令牌。如果用戶使用過期的訪問令牌,則該會(huì)話將被視為非活動(dòng)狀態(tài),并且需要新的訪問令牌。是否可以使用刷新令牌獲取此令牌或者需要新的身份驗(yàn)證輪次是由開發(fā)團(tuán)隊(duì)的要求定義的。

上文摘抄自[刷新令牌:何時(shí)使用它們以及它們?nèi)绾闻cJWT交互

廢話一堆,簡單的來說就是:

服務(wù)器生成token的過程中,會(huì)有兩個(gè)時(shí)間,一個(gè)是token失效時(shí)間,一個(gè)是token刷新時(shí)間,刷新時(shí)間肯定比失效時(shí)間長,當(dāng)用戶的token過期時(shí),你可以拿著過期的token去換取新的token,來保持用戶的登陸狀態(tài),當(dāng)然你這個(gè)過期token的過期時(shí)間必須在刷新時(shí)間之內(nèi),如果超出了刷新時(shí)間,那么返回的依舊是 401

所以要實(shí)現(xiàn)無痛刷新token,我們應(yīng)該這樣

在axios的攔截器中加入token刷新邏輯

當(dāng)用戶token過期時(shí),去向服務(wù)器請求新的 token

把舊的token替換為新的token

然后繼續(xù)用戶當(dāng)前的請求

用戶體驗(yàn)棒棒噠??

上代碼

在axios的攔截器中加入token刷新邏輯

instance.interceptors.response.use(function(response){// 對響應(yīng)數(shù)據(jù)做點(diǎn)什么returnresponse.data},function(error){console.log(error)if(error.response){if(error.response.status===401){// 如果當(dāng)前路由不是login,并且用戶有 “記住密碼” 的操作// 那么去請求新 tokenif(router.currentRoute.name!=='login'){if(getRemember()&&getRefreshToken()){returndoRequest(error)}else{Message.error('登陸過期請重新登陸!')setToken('')router.push({name:'login'})}}}}// 對響應(yīng)錯(cuò)誤做點(diǎn)什么returnPromise.reject(error.response)})

asyncfunctiondoRequest(error){constdata=awaitstore.dispatch('refreshToken')returnres}// refreshToken 中重新設(shè)置了 token 和? refresh_tokencommit('setToken',{token,expiresIn})setRefreshToken(token,refreshTtl/(60*60*24))

來看測試

為了方便測試,我們手動(dòng)清除了 token 來造成token 過期的效果

need-to-insert-img

refresh

可以看到手動(dòng)清除token之后,系統(tǒng)自動(dòng)去refresh了token,而不是跳到登錄頁面

但是問題又來了

可以看出用戶本身要去請求 articles 的接口,并沒有再重復(fù)請求。請腦補(bǔ):用戶點(diǎn)擊了文章列表,但是系統(tǒng)好像 “沒反應(yīng)” ???

所以接下來,我們不僅要刷新token而且要再次發(fā)送用戶上次的請求

上代碼??

asyncfunctiondoRequest(error){constdata=awaitstore.dispatch('refreshToken')let{token_type:tokenType,access_token:accessToken}=datalettoken=tokenType+accessTokenletconfig=error.response.config? ? config.headers.Authorization=tokenconstres=awaitaxios.request(config)returnres}

這里我們一定要用同步的方法來進(jìn)行這一系列操作!?。ū热?async / await)

來看演示??

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

相關(guān)閱讀更多精彩內(nèi)容

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