用vue做一個簡單的登錄鑒權功能。
項目目錄結構如下:
Login 組件
登錄成功后做本地存儲和store存儲,並進行跳轉。
Login.vue關鍵代碼:
async handleLogin(e) { e.preventDefault(); let parmas = { username: this.model.username, passwold: this.model.passwold }; const res = await this.$http.get("/api/login", parmas); const { code, token, massage } = res.data; //code=='0'表示登錄成功,進行本地存儲和store存儲 並進行跳轉。 //else 彈出錯誤提示 if (code == "0") { this.$store.commit("setToken", res.data.token); localStorage.setItem("token", token); //如果是由需要鑒權的頁面跳轉到登錄頁面 則redirect= this.$route.query.redirect,如果是直接點擊登錄跳轉到登錄頁面,則redirect= '/' const redirect = this.$route.query.redirect || "/"; this.$router.push(redirect); } else { const toast = this.$createToast({ time: 2000, txt: massage || "登錄失敗", type: "error" }); toast.show(); } }
store
在Login組件里登錄時token做了數據持久化處理,防止頁面刷新丟失token。給store里的token賦初值的時候要取
store.js關鍵代碼:
import Vue from 'vue' import Vuex from 'vuex' Vue.use(Vuex) export default new Vuex.Store({ state: { //token數據持久化,防止頁面刷新丟失 token: localStorage.getItem('token') || '' }, mutations: { setToken(state, token) { state.token = token } }, actions: { }, getters: { //根據token是否存在,設置計算屬性isLogin isLogin(state) { return !!state.token } } })
router
routes[]里用 mate.auth來標識是否需要鑒權。router.beforeEach做個全局路由守衛,根據是否需要鑒權以及是否已經登錄來進行不同操作。
router.js代碼:
import Vue from 'vue' import Router from 'vue-router' import Home from './views/Home.vue' import Login from './views/Login.vue'; import store from './store'; Vue.use(Router) const router = new Router({ mode: 'history', base: process.env.BASE_URL, routes: [ { path: '/', name: 'home', component: Home }, { path: '/login', name: 'login', component: Login }, { path: '/about', name: 'about', meta: { auth: true },//about需要做登錄鑒權 component: () => import(/* webpackChunkName: "about" */ './views/About.vue') } ] }) router.beforeEach((to, from, next) => { //to.meta.auth 表示需要做登錄健全 //不需要的 可以直接next if (to.meta.auth) { //store.state.token 表示已經登錄 可以直接next //沒有登錄 跳轉到/login 並攜帶參數redirect 方便登錄后直接跳轉到to.path if (store.state.token) { next(); } else { next({ path: '/login', query: { redirect: to.path } }) } } else { next(); } }) export default router;
axios攔截
axios.interceptors.request.use攔截axios所有http請求,如果存在token,則放入請求頭。axios.interceptors.response.use攔截的axios的響應,如果token以及失效,則清除本地緩存和store存儲並跳轉到登錄頁面。
http-interceptors.js代碼:
import axios from "axios"; import store from "./store"; import router from "./router"; // 攔截axios所有http請求,預先放入token請求頭 axios.interceptors.request.use(config => { if (store.state.token) { // 若存在token,則放入請求頭 config.headers.token = store.state.token; } return config; }); // 響應攔截器,提前預處理響應 axios.interceptors.response.use( response => { // 如果code是-1,說明用戶已注銷或者token已過期 // 此時需要重新登錄,並且還要清除本地緩存信息和store數據 if (response.status == 200) { const data = response.data; if (data.code == -1) { logoutFun() } } return response; }, err => { if (err.response.status === 401) { // 未授權 logoutFun() } } ); function logoutFun() { // 清空本地緩存的token和store里的token store.commit("setToken", ""); localStorage.removeItem("token"); // 跳轉至登錄頁,並攜帶用戶退出時或token失效時的頁面路由,方便登錄后直接跳轉到此頁面。 router.push({ path: "/login", query: { redirect: router.currentRoute.path } }); }
服務端也需要做請求處理的中間件。如果請求不是req.path不是'/api/login'並且沒有攜帶token,則返回錯誤狀態碼401。
vue.config.js關鍵代碼:
app.use((req, res, next) => { //只對api開頭的請求做攔截處理 if (/^\/api/.test(req.path)) { if (req.path == '/api/login' || req.headers.token) { next(); } else { //設置錯誤狀態碼為401 res.sendStatus('401') next(); } } else { next(); } })