登錄模塊
需要做路由的守衛,有些模塊是需要被保護的,必須登錄才能訪問。 常見手法是給路由添加 meta:{ auth:true } 來做標識,表示需要做認證。
// 路由守衛
router.beforeEach((to, from, next) => {
// 判斷要進入的路由是否需要認證
if(to.meta.auth) {
// 通過token令牌機制判斷是否已經登錄
const token = localStorage.getItem('token');
if (token) {
next(); // 如果登錄則放行,進入路由
} else {
// 跳轉,並攜帶重定向地址
next({
path: '/login',
query: {
redirect: to.path
}
});
}
} else {
// 不需要驗證的模塊,直接放行
next();
}
});
在 Vuex 中存儲登錄狀態 isLogin
import Vue from 'vue'
import Vuex from 'vuex'
import user from './service/user'
Vue.use(Vuex);
export default new Vuex.Store({
state: {
isLogin: localStorage.getItem('token') ? true : false
},
mutations: {
setLoginState(state, bool) {
state.isLogin = bool;
// true 表示登錄,false 表示注銷
}
},
actions: {
login({commit}, user) {
// 發起登錄請求,請求都拆分出去 service 文件夾中
// 使用 service 的請求方法
user.login(user).then(res => {
// 從 res.data 中解構數據,code和token
const { code, token } = res.data;
if (code) {
// 登錄成功,修改Vuex中間的登錄狀態
commit('setLoginState', true);
// 緩存令牌 token
localStorage.setItem("token", token);
}
return code; // 返回code 給下一個 .then 使用
});
},
logout({ commit }) {
// 清除緩存
localStorage.removeItem('token');
// 重置狀態
commit('setLoginState', false);
}
}
});
service文件夾下的服務
// 返回一個 Promises 方便外面使用
import axios from 'axios'
export default {
login(user) {
// 注意:請求地址最好是抽出去統一管理
return axios.get('/api/login', {
params: user
});
}
}
登錄組件中提交事件處理過程
// 提交函數中, dispatch 是調用 actios 中的方法,在通過 commit 發送 mutations 方法修改數據
// 注意:this.model 就是傳遞的數據
this.$store.dispatch("login", this.model)
.then(code => {
if (code) {
// 登錄成功后,重定向,如果沒有則重定向到首頁
const path = this.$route.query.redirect || '/';
this.$router.push(path);
}
}).catch(error => {
// 有錯誤發生 或 登錄失敗
const toast = this.$createToast({
time: 2000,
text: error.message || error.response.data.message || "登錄失敗",
type: "error"
});
toast.show();
});
需要考慮的檢查點
- 如何守衛路由
- 如何進行異步操作
- 如何保存登錄狀態
- 如何模擬接口
HTTP 攔截器
新建 interceptor.js 文件,用於攔截請求和響應
// token 過期導致請求失敗的情況可能出現在項目的任何地方,可以通過響應攔截統一處理
// 攔截器的作用:是對接口做一層保護,表示所有的接口都會帶有令牌 token
// 可以查看 axios 的文檔 : http://www.axios-js.com/zh-cn/docs/
const axios = require('axios');
export default function(vm) {
// HTTP 請求攔截器
axios.interceptors.request.use(config => {
// 在發送請求之前做些什么
// 獲取token, 並添加到 headers 請求頭中
const token = localStorage.getItem('token');
if (token) {
config.headers.token = token;
}
return config;
});
// HTTP 響應攔截器
// 統一處理 401 狀態,token 過期的處理,清除token跳轉login
// 參數 1, 表示成功響應
axios.interceptors.response.use(null, err => {
// 沒有登錄或令牌過期
if (err.response.status === 401) {
// 注銷,情況狀態和token
vm.$store.dispatch("logout");
// 跳轉的登錄頁
vm.$storer.push('/login');
// 注意: 這里的 vm 實例需要外部傳入
}
return Promise.reject(err);
});
}
// 使用攔截器
// 1. 引入攔截器文件
import interceptor from './interceptor'
// 執行攔截器初始化
interceptor(vm);
注銷
- 需要清除 token 緩存的兩種情況:
- 用戶主動注銷
- token 過期
- 需要做的事情:
- 清空緩存
- 重置登錄狀態
methods: {
logout() {
this.$route.dispatch('login');
}
}
深入理解令牌機制
-
Bearer Token規范
- 概念: 描述在HTTP訪問OAuth2保護資源時如何使用令牌的規范
- 特點:令牌就是身份證,無需證明令牌的所有權
- 具體規定:在請求頭中定義Auuthorization:Bearer token
-
Json Web Token 規范 https://jwt.io
- 概念:令牌的具體定義方式
- 規定:令牌由三部分構成 頭、載荷、簽名
- 頭:包含加密算法、令牌類型等信息
- 載荷:包含用戶信息、簽發時間和過期時間等信息
- 簽名:根據頭、載荷及秘鑰加密得到的哈希串
