今天我們來說一說前后端分離中的無痛刷新token機制,在手機app中應該經常用到,
大家都知道在前后端是以token的形式交互,既然是token,那么肯定有它的過期時間,沒有一個token是永久的,永久的token就相當于一串永久的密碼,是不安全的,
那么既然有刷新時間,問題就來了
前后端交互的過程中token如何存儲?
token過期時,前端該怎么處理
當用戶正在操作時,遇到token過期該怎么辦?直接跳回登陸頁面???(你確定這樣用戶不會打死你嗎,老子好不容易表單填完準備提交????)
token如何存儲
cookie的大小約4k,兼容性在ie6及以上? ? 都兼容,在瀏覽器和服務器間來回傳遞,因此它得在服務器的環境下運行,而且可以設定過期時間,默認的過期時間是session會話結束。
localStorage的大小約5M,兼容性在ie7及以上都兼容,有瀏覽器就可以,不需要在服務器的環境下運行, 會一直存在,除非手動清除 。
對于這個問題,答案大致分為2種
存在cookie中
存在localStorage中
我覺得都可以,兩種我都用??
token過期時,前端該怎么處理
思路:token過期處理方式大概就是:
第一種:跳回登陸頁面重新登陸
第二種:catch401 ,然后重新獲取token
對于第一種,很簡單在vue中我們可以在axios攔截器中這樣寫:
instance.interceptors.response.use(function(response){// 對響應數據做點什么returnresponse.data},function(error){console.log(error)if(error.response){if(error.response.status===401){Message.error('登陸過期請重新登陸!')setToken('')router.push({name:'login'})}}}// 對響應錯誤做點什么returnPromise.reject(error.response)})
對于第二種,如何重新獲取token,這就要涉及到后端的知識了
來先讓我講一段廢話??
現代認證和/或授權解決方案已將令牌的概念引入其協議中。令牌是經過特殊處理的數據,它們可以提供足夠的信息來授權用戶執行操作,或者允許客戶端獲取有關授權過程的其他信息(然后完成它)。換句話說,令牌是允許執行授權過程的信息。客戶端(或授權服務器以外的任何一方)是否可讀取或解析此信息是由實現定義的。重要的是:客戶端獲取此信息,然后使用它來訪問資源。JSON Web令牌(JWT)規范定義了一種可以由實現表示公共令牌信息的方式。
JWT定義了一種方式,其中可以表示與認證/授權過程有關的某些公共信息。顧名思義,數據格式是JSON。JWT具有某些常見字段,例如主題,發行者,到期時間等。當與其他規范(如JSON Web簽名(JWS)和JSON Web加密(JWE))結合使用時,JWT變得非常有用。這些規范不僅提供了授權令牌通常所需的所有信息,而且還提供了驗證令牌內容的方法,使其不會被篡改(JWS)和加密信息以使其保持不透明的方式給客戶(JWE)。數據格式的簡單性(及其它優點)幫助JWT成為最常見的令牌類型之一。如果您有興趣學習如何在Web應用程序中實現JWT,請查看Ryan Chenkie的這篇優秀文章。
出于本文的目的,我們將關注兩種最常見的令牌類型:訪問令牌和刷新令牌。
訪問令牌攜帶必要的信息以直接訪問資源。換句話說,當客戶端將訪問令牌傳遞給管理資源的服務器時,該服務器可以使用令牌中包含的信息來決定客戶端是否被授權。訪問令牌通常具有到期日期并且是短暫的。
訪問令牌
刷新令牌包含獲取新訪問令牌所需的信息。換句話說,每當訪問令牌需要訪問特定資源時,客戶端可以使用刷新令牌來獲得由認證服務器發布的新訪問令牌。常見用例包括在舊的訪問令牌過期后獲取新訪問令牌,或者首次訪問新資源。刷新令牌也可以過期,但相當長壽。刷新令牌通常受到嚴格的存儲要求,以確保它們不會泄露。它們也可以被授權服務器列入黑名單。
刷新令牌
標記是否不透明通常由實現定義。通用實現允許針對訪問令牌進行直接授權檢查。也就是說,當訪問令牌被傳遞到管理資源的服務器時,服務器可以讀取令牌中包含的信息并自己決定用戶是否被授權(不需要對授權服務器進行檢查)。這是必須簽署令牌的原因之一(例如,使用JWS)。另一方面,刷新令牌通常需要檢查授權服務器。這種處理授權檢查的分割方式允許三件事:
改進了授權服務器的訪問模式(降低負載,加快檢查速度)
泄漏訪問令牌的訪問窗口較短(這些訪問令牌很快過期,減少了泄露令牌允許訪問受保護資源的機會)
滑動會話(見下文)
滑動會話是在一段時間不活動后到期的會話。可以想象,使用訪問令牌和刷新令牌可以輕松實現。當用戶執行操作時,將發出新的訪問令牌。如果用戶使用過期的訪問令牌,則該會話將被視為非活動狀態,并且需要新的訪問令牌。是否可以使用刷新令牌獲取此令牌或者需要新的身份驗證輪次是由開發團隊的要求定義的。
上文摘抄自[刷新令牌:何時使用它們以及它們如何與JWT交互
廢話一堆,簡單的來說就是:
服務器生成token的過程中,會有兩個時間,一個是token失效時間,一個是token刷新時間,刷新時間肯定比失效時間長,當用戶的token過期時,你可以拿著過期的token去換取新的token,來保持用戶的登陸狀態,當然你這個過期token的過期時間必須在刷新時間之內,如果超出了刷新時間,那么返回的依舊是 401
所以要實現無痛刷新token,我們應該這樣
在axios的攔截器中加入token刷新邏輯
當用戶token過期時,去向服務器請求新的 token
把舊的token替換為新的token
然后繼續用戶當前的請求
用戶體驗棒棒噠??
上代碼
在axios的攔截器中加入token刷新邏輯
instance.interceptors.response.use(function(response){// 對響應數據做點什么returnresponse.data},function(error){console.log(error)if(error.response){if(error.response.status===401){// 如果當前路由不是login,并且用戶有 “記住密碼” 的操作// 那么去請求新 tokenif(router.currentRoute.name!=='login'){if(getRemember()&&getRefreshToken()){returndoRequest(error)}else{Message.error('登陸過期請重新登陸!')setToken('')router.push({name:'login'})}}}}// 對響應錯誤做點什么returnPromise.reject(error.response)})
asyncfunctiondoRequest(error){constdata=awaitstore.dispatch('refreshToken')returnres}// refreshToken 中重新設置了 token 和? refresh_tokencommit('setToken',{token,expiresIn})setRefreshToken(token,refreshTtl/(60*60*24))
來看測試
為了方便測試,我們手動清除了 token 來造成token 過期的效果
need-to-insert-img
refresh
可以看到手動清除token之后,系統自動去refresh了token,而不是跳到登錄頁面
但是問題又來了
可以看出用戶本身要去請求 articles 的接口,并沒有再重復請求。請腦補:用戶點擊了文章列表,但是系統好像 “沒反應” ???
所以接下來,我們不僅要刷新token而且要再次發送用戶上次的請求
上代碼??
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}
這里我們一定要用同步的方法來進行這一系列操作!!(比如 async / await)
來看演示??