Vue 專案中使用者登入及 token 驗證的思路

語言: CN / TW / HK

theme: juejin highlight: agate


前言

在講解 token 驗證之前,我們先來聊聊 vue-router 的導航守衛與 axios 攔截器這兩個知識點。

vue-router 導航守衛

所謂“導航”,即路由正在發生變化。可在路由跳轉時完成一些操作,而 router.beforeEach() 全域性前置守衛可以在路由跳轉前對現在狀態進行校驗,例如驗證使用者的登入狀態,若未登入則可以有效進行攔截。

```js const router = new VueRouter({ ... })

router.beforeEach((to, from, next) => { // ... }) ```

axios 攔截器

axios 攔截器又分為請求攔截器響應攔截器,可在請求或響應被 thencatch 處理前攔截它們,即在前端頁面向後端傳送請求時觸發進行攔截。

```js // 新增請求攔截器 axios.interceptors.request.use(function (config) { // 在傳送請求之前做些什麼 return config; }, function (error) { // 對請求錯誤做些什麼 return Promise.reject(error); });

// 新增響應攔截器 axios.interceptors.response.use(function (response) { // 2xx 範圍內的狀態碼都會觸發該函式。 // 對響應資料做點什麼 return response; }, function (error) { // 超出 2xx 範圍的狀態碼都會觸發該函式。 // 對響應錯誤做點什麼 return Promise.reject(error); }); ```

如果你稍後需要移除攔截器,可以這樣:

js const myInterceptor = axios.interceptors.request.use(function () {/*...*/}); axios.interceptors.request.eject(myInterceptor);

區別

導航守衛更像是路由級攔截,而 axios 攔截器則是介面級攔截,這是它們本質上的區別。

關於它們更詳細的區分在後文會提及,這裡先不討論,因為知識點沒有帶入到例項場景中進行應用容易造成理解不深或理解不了的問題,先回歸正文。

Vue 專案實現 token 驗證

401 Unauthorized:該狀態碼錶示使用者所傳送的請求沒有訪問許可權,需要進行身份認證。返回含有 401 的響應必須包含一個適用於被請求資源的 WWW-Authenticate 首部用以質詢使用者資訊。

前後端分離開發方式固然好,但也帶來了一些棘手的問題,如跨域處理token 驗證..

接下來展開說說 token 驗證。

在 Vue 專案中實現 token 驗證的大致思路如下:

  1. 第一次登入時,前端呼叫後端的登入介面,傳送使用者名稱和密碼
  2. 後端收到請求後,驗證使用者名稱和密碼的合法性,成功則給前端返回一個 token
  3. 前端拿到 token 後,將其儲存到 localStoragevuex 中,並跳轉路由頁面
  4. 此後前端每次跳轉路由時,就判斷 localStorage 中有無 token,沒有則跳轉到登入頁面,有則跳轉到對應的路由頁面
  5. 且前端每次呼叫後端介面時,都要在請求頭中攜帶 token
  6. 後端判斷請求頭中有無 token
  7. header 中存在 token:驗證 token 的合法性,成功則返回前端請求的資料;失敗(如 token 過期)就返回 401 狀態碼(401 Unauthorized
  8. header 中不存在 token:同樣返回 401 狀態碼
  9. 如果前端接收到 401 狀態碼,則清除 token 資訊並跳轉到登陸頁面

Login.vue

```js

```

store/index.js

```js import Vue from 'vue' import Vuex from 'vuex' Vue.use(Vuex)

const store = new Vuex.Store({ state: { // 儲存token Authorization: localStorage.getItem('Authorization') ? localStorage.getItem('Authorization') : '' }, mutations: { // 修改token,並將token存入localStorage changeLogin (state, user) { state.Authorization = user.Authorization; localStorage.setItem('Authorization', user.Authorization); } } });

export default store ```

router/index.js: 導航守衛

```js import Vue from 'vue'; import Router from 'vue-router'; import login from '@/components/login'; import home from '@/components/home';

Vue.use(Router);

const router = new Router({ routes: [ { path: '/', redirect: '/login' }, { path: '/login', name: 'login', component: login }, { path: '/home', name: 'home', component: home } ] });

// 導航守衛:使用 router.beforeEach 註冊一個全域性前置守衛,判斷使用者是否登陸 router.beforeEach((to, from, next) => { if (to.path === '/login') { next(); } else { let token = localStorage.getItem('Authorization');

if (token === null || token === '') {
  next('/login');
} else {
  next();
}

} });

export default router ```

main.js: 請求攔截器

js // 請求攔截器, 每次請求都會在請求頭中攜帶token axios.interceptors.request.use((config) => { if(localStorage.getItem('Authorization')) { config.headers.Authorization = localStorage.getItem('Authorization') } return config; }, (error) => { return Promise.reject(error); })

401 Unauthorized 處理

如果前端拿到後端返回的 401 狀態碼,則清除 token 資訊並跳轉到登陸頁面。

js localStorage.removeItem('Authorization'); this.$router.push('/login');

導航守衛請求攔截器

導航守衛僅僅簡單判斷是否有 token 值存在(不管該 token 是否有效),如果不存在/失效就進行重定向。

請求攔截器是向後端傳送請求並校驗,如果 token 合法就訪問成功,否則訪問失敗並進行重定向。

⭐總之,在進行頁面許可權判斷時通常都是讓 vue-router 導航守衛axios 攔截器搭配使用!

FAQ

Q:為什麼不在導航守衛中順便也傳送請求來校驗 token 是否過期,這樣 token 過期後在使用者進行頁面跳轉時不就可以及時提醒使用者重新登入了嗎?
A:token 本身就是用於進行介面鑑權,token 過期以後,就無法再請求資料了。在如今單頁面應用中,訪問有的頁面時不需要和後端進行互動,即沒有介面請求,那跟 token 介面鑑權無關,就無需進行校驗。而只要經過了攔截器必然就是一次介面的請求,所以是給攔截器進行驗證是否過期。導航守衛做驗證當然可以,不過會因為給沒有互動的頁面做驗證,而造成無效的資源浪費


🙎‍♂️部落格引用: