Vue项目使用拦截器和JWT验证 完整案例
挺詳細的一個案例項目,值得參考!
作者:YXi
https://juejin.im/post/6844903959883218951)
https://gitee.com/gitee_fanjunyang/Inter_JWT
幾乎在所有的項目中都離不開攔截器和登錄驗證,這是必需的。如果你學會了這個 demo,那么幾乎所有網站的登錄驗證,加載動畫就都會了,所以背也要背會
所以本章以一個 demo 為例,來幫助大家理解攔截器和登錄驗證控制
文章后面有源碼,可以下載下來運行一下
先來看看效果:
功能:
-
當你訪問首頁的時候,會有一個加載動畫,就是攔截器的功勞,并且首頁會有一個當前登錄的用戶名,默認是 wangcai,等你登錄成功后,會替換成你自己登錄的用戶名
-
當你沒有登錄的時候,可以訪問首頁和登錄頁,但是訪問不了個人中心 (Profile),當你訪問個人中心,會給你自動跳轉到登錄頁
-
當你在登錄頁進行登錄,如果用戶名輸入錯誤的話,會彈出錯誤信息
-
當你輸入正確的時候 (我設置了 Fan 為正確的用戶名),點擊登錄,登錄成功后,會自動給你跳轉到首頁
-
并且登錄成功后,如果你再點擊想進入登錄頁,是不行的,他會自動給你跳轉到首頁
-
登錄成功后,就可以訪問 個人中心頁面
-
如果你超過 20 秒 不對頁面進行操作 (我設置的是 20 秒,可以自行設置),那么 token 會自動失效,那么你就訪問不了個人中心,你需要再次登錄
-
如果你在 20 秒 之內,操作頁面的話,那么 token 的值是不會失效的,所以是不需要再次登錄的。也就是說,在 20 秒 之內,你每次進行路由跳轉的時候,token 的值和時間就會自動重置,防止失效讓你再次登錄(總不能讓你看著看著突然讓你登錄)
-
搜索公縱號:MarkerHub,關注回復[?vue?]獲取前后端入門教程!
下面就讓我們開始吧!!!(有關代碼的解釋說明已在代碼中注釋)
案例
使用攔截器并封裝axios
新建一個 Vue 項目 (vue create demo)
刪去不必要的文件和代碼,經典化代碼
安裝需要的依賴:
package.json文件部分代碼:
"dependencies": {"axios": "^0.19.0","body-parser": "^1.19.0","core-js": "^2.6.5","express": "^4.17.1","iview": "^4.0.0-rc.4","jsonwebtoken": "^8.5.1","vue": "^2.6.10","vue-router": "^3.0.3","vuex": "^3.0.1" },在server.js文件中配置跨域,并書寫測試接口:
let express = require('express') let bodyParser = require('body-parser')let app = express()// 配置跨域 app.use((req, res, next) => {res.header("Access-Control-Allow-Origin", "\*");res.header("Access-Control-Allow-Methods", "GET,HEAD,OPTIONS,POST,PUT"),res.header("Access-Control-Allow-Headers", "Origin,X-Requested-With,Content-Type,Accept,Authorization")if (req.method.toLowerCase() === "options") {return res.end();}next(); })// 配置bodyparser app.use(bodyParser.json())app.get("/user", (req, res) => {//在請求數據時,要加一個動畫,為了測試,所以讓它時間長點,加了一個定時器setTimeout(() => {res.json({name: "wangcai"})}, 500) })app.listen(3000)在router.js中配置路由:
routes: \[{path: '/',name: 'home',component: Home},{path: '/login',name: 'login',component: () => import('./views/Login.vue')},{path: '/profile',name: 'profile',component: () => import('./views/Profile.vue')} \]因為項目中需要用到樣式什么的,這里我引入了iView,main.js代碼:
import Vue from 'vue' import App from './App.vue' import router from './router' import store from './store'//引入iView import iView from 'iview' import 'iview/dist/styles/iview.css'; Vue.use(iView)Vue.config.productionTip = falsenew Vue({router,store,render: h => h(App) }).$mount('#app')因為項目中要用到加載數據的動畫,所以需要在store.js中的state中配置:
state: {//定義動畫是否顯示isShowLoading:false,username:'wangcai'},mutations: {//使動畫顯示showLoading(state){state.isShowLoading = true;},//使動畫隱藏hideLoading(state){state.isShowLoading = false;} },在App.vue中配置跳轉:
<template><div><div><router-link to="/">Home</router-link> |<router-link to="/login">Login</router-link> |<router-link to="/profile">Profile</router-link></div><router-view/></div> </template>Home.vue代碼:
<template><div><h1>首頁面</h1></div> </template>Login.vue代碼:
<template><div><i-input placeholder="請輸入用戶名..."></i-input><i-button type="primary">登錄</i-button></div> </template>Profile.vue代碼:
<template> <div><h1>個人中心</h1> </div> </template>然后在libs文件夾下面新建一個ajaxRequest.js文件,用來封裝我們自己的 axios 和 加載動畫 等
搜索公縱號:MarkerHub,關注回復[?vue?]獲取前后端入門教程!
import axios from 'axios' import store from '../store'//當第一次請求時,顯示loading class AjaxRequest {//當new的時候,調用這個方法constructor() {//請求的基礎路徑this.baseURL = process.env.NODE\_ENV == "production" ? "/" : "http://localhost:3000"this.timeout = 3000 //超時時間this.queue = {} //存放每一次的請求}//定義一個方法,把options展開merge(options) {return {...options,baseURL: this.baseURL,timeout: this.timeout}}//封裝一個攔截方法setInterceptor(instance, url) {//請求攔截,每次請求時,都要加上一個loading效果instance.interceptors.request.use((config) => {//每次請求時,都給他加一個Authorization頭,在JWT驗證時要用config.headers.Authorization = 'xxx'//第一次請求時,顯示loading動畫if (Object.keys(this.queue).length === 0) {store.commit('showLoading')}this.queue\[url\] = url;return config})//響應攔截instance.interceptors.response.use((res) => {//刪除queue里面的鏈接,如果同一個按鈕,你一秒之內點擊無數次,但是他只處理第一次操作delete this.queue\[url\]//隱藏loading動畫if (Object.keys(this.queue).length === 0) {store.commit('hideLoading')}//返回的結果return res.data})}request(options) {let instance = axios.create() //創建一個axios實例this.setInterceptor(instance, options.url) //設置攔截let config = this.merge(options)return instance(config) //axios執行后,返回promise} }export default new AjaxRequest;然后在api文件夾下新建一個user.js文件用來放用戶相關的調用接口的方法(當你想要調用接口的時候,直接調用里面的方法就好):
import axios from '../libs/ajaxRequset'// 用戶相關的接口 export const getUser = ()=>{return axios.request({url:'/user',method:'get'}) }修改Home.vue中的代碼:
<template><div><h1>首頁面</h1><p>當前登錄的用戶名是{{$store.state.username}}</p></div> </template><script> //如果用export導出的話,要用這種形式,相當于解構賦值 import {getUser} from '../api/user' export default {name:'home',async mounted(){let r = await getUser()console.log(r);} } </script>修改App.vue中的代碼(加動畫效果):
<template><div><Spin size="large" fix v-if="$store.state.isShowLoading">加載中...</Spin><div><router-link to="/">Home</router-link> |<router-link to="/login">Login</router-link> |<router-link to="/profile">Profile</router-link></div><router-view/></div> </template><style> #app {font-family: "Avenir", Helvetica, Arial, sans-serif;-webkit-font-smoothing: antialiased;-moz-osx-font-smoothing: grayscale;text-align: center;color: #2c3e50; } #nav {padding: 30px; }#nav a {font-weight: bold;color: #2c3e50; }#nav a.router-link-exact-active {color: #42b983; } </style>運行效果:
使用 JWT
在server.js中新增代碼,使用 JWT,并寫好登錄和驗證的接口:
let jwt = require('jsonwebtoken')let secret = "xwc"//登錄的接口 app.post('/login',(req,res)=>{let {username} = req.body;if(username === 'Fan'){//登錄成功后返回一個tokenres.json({code:0,username:'Fan',token:jwt.sign({username:'Fan'},secret,{expiresIn:20 //表示token20秒過期})})}else{//登錄失敗res.json({code:1,data:'登錄失敗了'})} })//驗證token的接口 app.get('/validate',(req,res)=>{let token = req.headers.authorization; //我們會把token放到我們自己設置的http的頭authorization中,在這里可以直接拿到jwt.verify(token,secret,(err,decode)=>{ //驗證tokenif(err){return res.json({code:1,data:'token失效了'})}else{// token合法 在這里,需要把token的時效延長,//總不能我們看著看著突然讓我們重新登錄,token過期的意思是,你在這之間不進行任何操作才會過期res.json({code:0,username:decode.username,token:jwt.sign({username:'Fan'},secret,{ //合法時,我們需要重新生成一個token,我們每次切換路由,都要重新生成一個tokenexpiresIn:20})})}}) })接著在api文件夾下的user.js文件中添加登錄和驗證的方法:
import axios from '../libs/ajaxRequest'// 用戶相關的接口// 調獲取用戶信息的接口 向外暴露一個getUser方法 這個方法中調了接口 // 在組件中,就可以使用getUser,就相當于調用接口 export const getUser = ()=>{return axios.request({url:'/user',method:'get'}) }// 再向外暴露一個登錄的方法,方法內部也是調接口 // 在登錄組件中就可以調用Login方法,需要給方法傳遞一個用戶名 export const login = (username)=>{return axios.request({url:'/login',method:'post',data:{username}}) }//驗證token方法 export const validate = ()=>{return axios.request({url:'/validate',method:'get'}) }接著我們在lib文件夾下新建一個local.js文件,用來設置或者獲取localStorage里的token:
//把獲得到的token存到localStorage里 export const setLocal = (key,value)=>{if(typeof value == 'object'){ //如果傳過來的是對象,則轉換成字符串value = JSON.stringify(value)}localStorage.setItem(key,value) //存到localStorage里 }//獲取localStorage里的token export const getLocal = (key)=>{return localStorage.getItem(key) }然后修改store.js中的代碼:
import Vue from 'vue' import Vuex from 'vuex' import {login,validate} from './api/user' //必須用這種方式引入 import {setLocal} from './libs/local' //引入lib文件夾下的local.js文件中的setLocal方法(往localStorage里存放token)Vue.use(Vuex)export default new Vuex.Store({state: {//定義動畫是否顯示isShowLoading:false,username:'wangcai'},mutations: {//使動畫顯示showLoading(state){state.isShowLoading = true;},//使動畫隱藏hideLoading(state){state.isShowLoading = false;},//定義修改用戶名的方法setUser(state,username){state.username = username}},// actions存放接口的調用 dispatch actions里面放方法actions: {//這里面所有的方法都是異步的//登錄方法async toLogin({commit},username){let r = await login(username) //調用user.js中的login方法,也就是調用登錄接口// console.log(r);if(r.code === 0){ //登錄成功后會給你返回json數據,里面有code//登錄成功了commit('setUser',r.username) //修改用戶名setLocal('token',r.token) //把得到的token存到localStorage里}else{// console.log('............');return Promise.reject(r.data); //如果失敗,返回一個promise失敗態}},//驗證token方法async validate({commit}){let r = await validate(); //調用user.js中的validate方法,也就是調用驗證接口if(r.code === 0){commit('setUser',r.username)setLocal('token',r.token) //我們說了,驗證通過,或者每次切換路由時,都要重新生成token}return r.code === 0; //返回token是否失效,true或者false}} })修改Login.vue中的代碼:
<template><div><i-input v-model="username" placeholder="請輸入用戶名..."></i-input><i-button type="primary" @click="login()">登錄</i-button></div> </template><script> import {mapActions} from 'vuex' //使用vuex中的mapActions方法,不會的請參考我的文章vuex的使用方法 export default {data(){return{username:'' //定義一個用戶名}},methods:{...mapActions(\['toLogin'\]), //獲取store.js文件中的actions中的toLogin方法login(){// console.log(this\['toLogin'\](this.username));//使用獲取到的toLogin方法this\['toLogin'\](this.username).then(data=>{ //因為toLogin返回的是一個Promise,所以可以.thenthis.$router.push('/') //登錄成功,跳到首頁面},err=>{this.$Message.error(err)})}} } </script>別忘了修改ajaxRequest.js文件,在請求攔截的時候,需要加個頭,前面我們寫死了,這里,要把 token 給他,然后每次路由跳轉訪問頁面的時候,都會帶上這個頭,用來驗證:
import {getLocal} from "../libs/local" //引入//請求攔截,每次請求時,都要加上一個loading效果 instance.interceptors.request.use((config) => {//每次請求時,都給他加一個Authorization頭,在JWT驗證時要用config.headers.Authorization = getLocal('token')//第一次請求時,顯示loading動畫if (Object.keys(this.queue).length === 0) {store.commit('showLoading')}this.queue\[url\] = url;return config })接著在router.js中設置路由:
哪個頁面需要登錄后才能訪問的話,給這個路由添加meta,假如我的 個人中心頁面 需要登錄后才能訪問,那么我需要修改代碼:
最后修改main.js中的代碼,當切換路由時,進行驗證:
import Vue from 'vue' import App from './App.vue' import router from './router' import store from './store'//引入iView import iView from 'iview' import 'iview/dist/styles/iview.css';Vue.use(iView)Vue.config.productionTip = false//每一次切換路由時,都執行這個導航守衛 router.beforeEach(async (to,from,next)=>{let isLogin = await store.dispatch('validate') //判斷是否登錄了// needLogin 表示哪些路由需要在登錄條件下才能訪問console.log(to);let needLogin = to.matched.some(match=>match.meta.needLogin)if(needLogin){//需要登錄if(isLogin){//登錄過了next()}else{//沒有登錄next('/login')}}else{//不需要登錄if(isLogin && to.path === '/login'){ //如果你訪問login頁面,則給你跳到首頁面,因為不需要登錄next('/')}else{next()}} })new Vue({router,store,render: h => h(App) }).$mount('#app')有關需要注意的點,都加注釋了,好好看注釋就行
至此,整個案例就結束了
?
挺詳細的一個案例項目,值得參考!
作者:YXi
https://juejin.im/post/6844903959883218951)
幾乎在所有的項目中都離不開攔截器和登錄驗證,這是必需的。如果你學會了這個 demo,那么幾乎所有網站的登錄驗證,加載動畫就都會了,所以背也要背會
所以本章以一個 demo 為例,來幫助大家理解攔截器和登錄驗證控制
文章后面有源碼,可以下載下來運行一下
先來看看效果:
功能:
-
當你訪問首頁的時候,會有一個加載動畫,就是攔截器的功勞,并且首頁會有一個當前登錄的用戶名,默認是 wangcai,等你登錄成功后,會替換成你自己登錄的用戶名
-
當你沒有登錄的時候,可以訪問首頁和登錄頁,但是訪問不了個人中心 (Profile),當你訪問個人中心,會給你自動跳轉到登錄頁
-
當你在登錄頁進行登錄,如果用戶名輸入錯誤的話,會彈出錯誤信息
-
當你輸入正確的時候 (我設置了 Fan 為正確的用戶名),點擊登錄,登錄成功后,會自動給你跳轉到首頁
-
并且登錄成功后,如果你再點擊想進入登錄頁,是不行的,他會自動給你跳轉到首頁
-
登錄成功后,就可以訪問 個人中心頁面
-
如果你超過 20 秒 不對頁面進行操作 (我設置的是 20 秒,可以自行設置),那么 token 會自動失效,那么你就訪問不了個人中心,你需要再次登錄
-
如果你在 20 秒 之內,操作頁面的話,那么 token 的值是不會失效的,所以是不需要再次登錄的。也就是說,在 20 秒 之內,你每次進行路由跳轉的時候,token 的值和時間就會自動重置,防止失效讓你再次登錄(總不能讓你看著看著突然讓你登錄)
-
搜索公縱號:MarkerHub,關注回復[?vue?]獲取前后端入門教程!
下面就讓我們開始吧!!!(有關代碼的解釋說明已在代碼中注釋)
案例
使用攔截器并封裝axios
新建一個 Vue 項目 (vue create demo)
刪去不必要的文件和代碼,經典化代碼
安裝需要的依賴:
package.json文件部分代碼:
"dependencies": {"axios": "^0.19.0","body-parser": "^1.19.0","core-js": "^2.6.5","express": "^4.17.1","iview": "^4.0.0-rc.4","jsonwebtoken": "^8.5.1","vue": "^2.6.10","vue-router": "^3.0.3","vuex": "^3.0.1" },在server.js文件中配置跨域,并書寫測試接口:
let express = require('express') let bodyParser = require('body-parser')let app = express()// 配置跨域 app.use((req, res, next) => {res.header("Access-Control-Allow-Origin", "\*");res.header("Access-Control-Allow-Methods", "GET,HEAD,OPTIONS,POST,PUT"),res.header("Access-Control-Allow-Headers", "Origin,X-Requested-With,Content-Type,Accept,Authorization")if (req.method.toLowerCase() === "options") {return res.end();}next(); })// 配置bodyparser app.use(bodyParser.json())app.get("/user", (req, res) => {//在請求數據時,要加一個動畫,為了測試,所以讓它時間長點,加了一個定時器setTimeout(() => {res.json({name: "wangcai"})}, 500) })app.listen(3000)在router.js中配置路由:
routes: \[{path: '/',name: 'home',component: Home},{path: '/login',name: 'login',component: () => import('./views/Login.vue')},{path: '/profile',name: 'profile',component: () => import('./views/Profile.vue')} \]因為項目中需要用到樣式什么的,這里我引入了iView,main.js代碼:
import Vue from 'vue' import App from './App.vue' import router from './router' import store from './store'//引入iView import iView from 'iview' import 'iview/dist/styles/iview.css'; Vue.use(iView)Vue.config.productionTip = falsenew Vue({router,store,render: h => h(App) }).$mount('#app')因為項目中要用到加載數據的動畫,所以需要在store.js中的state中配置:
state: {//定義動畫是否顯示isShowLoading:false,username:'wangcai'},mutations: {//使動畫顯示showLoading(state){state.isShowLoading = true;},//使動畫隱藏hideLoading(state){state.isShowLoading = false;} },在App.vue中配置跳轉:
<template><div><div><router-link to="/">Home</router-link> |<router-link to="/login">Login</router-link> |<router-link to="/profile">Profile</router-link></div><router-view/></div> </template>Home.vue代碼:
<template><div><h1>首頁面</h1></div> </template>Login.vue代碼:
<template><div><i-input placeholder="請輸入用戶名..."></i-input><i-button type="primary">登錄</i-button></div> </template>Profile.vue代碼:
<template> <div><h1>個人中心</h1> </div> </template>然后在libs文件夾下面新建一個ajaxRequest.js文件,用來封裝我們自己的 axios 和 加載動畫 等
搜索公縱號:MarkerHub,關注回復[?vue?]獲取前后端入門教程!
import axios from 'axios' import store from '../store'//當第一次請求時,顯示loading class AjaxRequest {//當new的時候,調用這個方法constructor() {//請求的基礎路徑this.baseURL = process.env.NODE\_ENV == "production" ? "/" : "http://localhost:3000"this.timeout = 3000 //超時時間this.queue = {} //存放每一次的請求}//定義一個方法,把options展開merge(options) {return {...options,baseURL: this.baseURL,timeout: this.timeout}}//封裝一個攔截方法setInterceptor(instance, url) {//請求攔截,每次請求時,都要加上一個loading效果instance.interceptors.request.use((config) => {//每次請求時,都給他加一個Authorization頭,在JWT驗證時要用config.headers.Authorization = 'xxx'//第一次請求時,顯示loading動畫if (Object.keys(this.queue).length === 0) {store.commit('showLoading')}this.queue\[url\] = url;return config})//響應攔截instance.interceptors.response.use((res) => {//刪除queue里面的鏈接,如果同一個按鈕,你一秒之內點擊無數次,但是他只處理第一次操作delete this.queue\[url\]//隱藏loading動畫if (Object.keys(this.queue).length === 0) {store.commit('hideLoading')}//返回的結果return res.data})}request(options) {let instance = axios.create() //創建一個axios實例this.setInterceptor(instance, options.url) //設置攔截let config = this.merge(options)return instance(config) //axios執行后,返回promise} }export default new AjaxRequest;然后在api文件夾下新建一個user.js文件用來放用戶相關的調用接口的方法(當你想要調用接口的時候,直接調用里面的方法就好):
import axios from '../libs/ajaxRequset'// 用戶相關的接口 export const getUser = ()=>{return axios.request({url:'/user',method:'get'}) }修改Home.vue中的代碼:
<template><div><h1>首頁面</h1><p>當前登錄的用戶名是{{$store.state.username}}</p></div> </template><script> //如果用export導出的話,要用這種形式,相當于解構賦值 import {getUser} from '../api/user' export default {name:'home',async mounted(){let r = await getUser()console.log(r);} } </script>修改App.vue中的代碼(加動畫效果):
<template><div><Spin size="large" fix v-if="$store.state.isShowLoading">加載中...</Spin><div><router-link to="/">Home</router-link> |<router-link to="/login">Login</router-link> |<router-link to="/profile">Profile</router-link></div><router-view/></div> </template><style> #app {font-family: "Avenir", Helvetica, Arial, sans-serif;-webkit-font-smoothing: antialiased;-moz-osx-font-smoothing: grayscale;text-align: center;color: #2c3e50; } #nav {padding: 30px; }#nav a {font-weight: bold;color: #2c3e50; }#nav a.router-link-exact-active {color: #42b983; } </style>運行效果:
使用 JWT
在server.js中新增代碼,使用 JWT,并寫好登錄和驗證的接口:
let jwt = require('jsonwebtoken')let secret = "xwc"//登錄的接口 app.post('/login',(req,res)=>{let {username} = req.body;if(username === 'Fan'){//登錄成功后返回一個tokenres.json({code:0,username:'Fan',token:jwt.sign({username:'Fan'},secret,{expiresIn:20 //表示token20秒過期})})}else{//登錄失敗res.json({code:1,data:'登錄失敗了'})} })//驗證token的接口 app.get('/validate',(req,res)=>{let token = req.headers.authorization; //我們會把token放到我們自己設置的http的頭authorization中,在這里可以直接拿到jwt.verify(token,secret,(err,decode)=>{ //驗證tokenif(err){return res.json({code:1,data:'token失效了'})}else{// token合法 在這里,需要把token的時效延長,//總不能我們看著看著突然讓我們重新登錄,token過期的意思是,你在這之間不進行任何操作才會過期res.json({code:0,username:decode.username,token:jwt.sign({username:'Fan'},secret,{ //合法時,我們需要重新生成一個token,我們每次切換路由,都要重新生成一個tokenexpiresIn:20})})}}) })接著在api文件夾下的user.js文件中添加登錄和驗證的方法:
import axios from '../libs/ajaxRequest'// 用戶相關的接口// 調獲取用戶信息的接口 向外暴露一個getUser方法 這個方法中調了接口 // 在組件中,就可以使用getUser,就相當于調用接口 export const getUser = ()=>{return axios.request({url:'/user',method:'get'}) }// 再向外暴露一個登錄的方法,方法內部也是調接口 // 在登錄組件中就可以調用Login方法,需要給方法傳遞一個用戶名 export const login = (username)=>{return axios.request({url:'/login',method:'post',data:{username}}) }//驗證token方法 export const validate = ()=>{return axios.request({url:'/validate',method:'get'}) }接著我們在lib文件夾下新建一個local.js文件,用來設置或者獲取localStorage里的token:
//把獲得到的token存到localStorage里 export const setLocal = (key,value)=>{if(typeof value == 'object'){ //如果傳過來的是對象,則轉換成字符串value = JSON.stringify(value)}localStorage.setItem(key,value) //存到localStorage里 }//獲取localStorage里的token export const getLocal = (key)=>{return localStorage.getItem(key) }然后修改store.js中的代碼:
import Vue from 'vue' import Vuex from 'vuex' import {login,validate} from './api/user' //必須用這種方式引入 import {setLocal} from './libs/local' //引入lib文件夾下的local.js文件中的setLocal方法(往localStorage里存放token)Vue.use(Vuex)export default new Vuex.Store({state: {//定義動畫是否顯示isShowLoading:false,username:'wangcai'},mutations: {//使動畫顯示showLoading(state){state.isShowLoading = true;},//使動畫隱藏hideLoading(state){state.isShowLoading = false;},//定義修改用戶名的方法setUser(state,username){state.username = username}},// actions存放接口的調用 dispatch actions里面放方法actions: {//這里面所有的方法都是異步的//登錄方法async toLogin({commit},username){let r = await login(username) //調用user.js中的login方法,也就是調用登錄接口// console.log(r);if(r.code === 0){ //登錄成功后會給你返回json數據,里面有code//登錄成功了commit('setUser',r.username) //修改用戶名setLocal('token',r.token) //把得到的token存到localStorage里}else{// console.log('............');return Promise.reject(r.data); //如果失敗,返回一個promise失敗態}},//驗證token方法async validate({commit}){let r = await validate(); //調用user.js中的validate方法,也就是調用驗證接口if(r.code === 0){commit('setUser',r.username)setLocal('token',r.token) //我們說了,驗證通過,或者每次切換路由時,都要重新生成token}return r.code === 0; //返回token是否失效,true或者false}} })修改Login.vue中的代碼:
<template><div><i-input v-model="username" placeholder="請輸入用戶名..."></i-input><i-button type="primary" @click="login()">登錄</i-button></div> </template><script> import {mapActions} from 'vuex' //使用vuex中的mapActions方法,不會的請參考我的文章vuex的使用方法 export default {data(){return{username:'' //定義一個用戶名}},methods:{...mapActions(\['toLogin'\]), //獲取store.js文件中的actions中的toLogin方法login(){// console.log(this\['toLogin'\](this.username));//使用獲取到的toLogin方法this\['toLogin'\](this.username).then(data=>{ //因為toLogin返回的是一個Promise,所以可以.thenthis.$router.push('/') //登錄成功,跳到首頁面},err=>{this.$Message.error(err)})}} } </script>別忘了修改ajaxRequest.js文件,在請求攔截的時候,需要加個頭,前面我們寫死了,這里,要把 token 給他,然后每次路由跳轉訪問頁面的時候,都會帶上這個頭,用來驗證:
import {getLocal} from "../libs/local" //引入//請求攔截,每次請求時,都要加上一個loading效果 instance.interceptors.request.use((config) => {//每次請求時,都給他加一個Authorization頭,在JWT驗證時要用config.headers.Authorization = getLocal('token')//第一次請求時,顯示loading動畫if (Object.keys(this.queue).length === 0) {store.commit('showLoading')}this.queue\[url\] = url;return config })接著在router.js中設置路由:
哪個頁面需要登錄后才能訪問的話,給這個路由添加meta,假如我的 個人中心頁面 需要登錄后才能訪問,那么我需要修改代碼:
最后修改main.js中的代碼,當切換路由時,進行驗證:
import Vue from 'vue' import App from './App.vue' import router from './router' import store from './store'//引入iView import iView from 'iview' import 'iview/dist/styles/iview.css';Vue.use(iView)Vue.config.productionTip = false//每一次切換路由時,都執行這個導航守衛 router.beforeEach(async (to,from,next)=>{let isLogin = await store.dispatch('validate') //判斷是否登錄了// needLogin 表示哪些路由需要在登錄條件下才能訪問console.log(to);let needLogin = to.matched.some(match=>match.meta.needLogin)if(needLogin){//需要登錄if(isLogin){//登錄過了next()}else{//沒有登錄next('/login')}}else{//不需要登錄if(isLogin && to.path === '/login'){ //如果你訪問login頁面,則給你跳到首頁面,因為不需要登錄next('/')}else{next()}} })new Vue({router,store,render: h => h(App) }).$mount('#app')有關需要注意的點,都加注釋了,好好看注釋就行
至此,整個案例就結束了
?
挺詳細的一個案例項目,值得參考!
作者:YXi
https://juejin.im/post/6844903959883218951)
幾乎在所有的項目中都離不開攔截器和登錄驗證,這是必需的。如果你學會了這個 demo,那么幾乎所有網站的登錄驗證,加載動畫就都會了,所以背也要背會
所以本章以一個 demo 為例,來幫助大家理解攔截器和登錄驗證控制
文章后面有源碼,可以下載下來運行一下
先來看看效果:
功能:
-
當你訪問首頁的時候,會有一個加載動畫,就是攔截器的功勞,并且首頁會有一個當前登錄的用戶名,默認是 wangcai,等你登錄成功后,會替換成你自己登錄的用戶名
-
當你沒有登錄的時候,可以訪問首頁和登錄頁,但是訪問不了個人中心 (Profile),當你訪問個人中心,會給你自動跳轉到登錄頁
-
當你在登錄頁進行登錄,如果用戶名輸入錯誤的話,會彈出錯誤信息
-
當你輸入正確的時候 (我設置了 Fan 為正確的用戶名),點擊登錄,登錄成功后,會自動給你跳轉到首頁
-
并且登錄成功后,如果你再點擊想進入登錄頁,是不行的,他會自動給你跳轉到首頁
-
登錄成功后,就可以訪問 個人中心頁面
-
如果你超過 20 秒 不對頁面進行操作 (我設置的是 20 秒,可以自行設置),那么 token 會自動失效,那么你就訪問不了個人中心,你需要再次登錄
-
如果你在 20 秒 之內,操作頁面的話,那么 token 的值是不會失效的,所以是不需要再次登錄的。也就是說,在 20 秒 之內,你每次進行路由跳轉的時候,token 的值和時間就會自動重置,防止失效讓你再次登錄(總不能讓你看著看著突然讓你登錄)
-
搜索公縱號:MarkerHub,關注回復[?vue?]獲取前后端入門教程!
下面就讓我們開始吧!!!(有關代碼的解釋說明已在代碼中注釋)
案例
使用攔截器并封裝axios
新建一個 Vue 項目 (vue create demo)
刪去不必要的文件和代碼,經典化代碼
安裝需要的依賴:
package.json文件部分代碼:
"dependencies": {"axios": "^0.19.0","body-parser": "^1.19.0","core-js": "^2.6.5","express": "^4.17.1","iview": "^4.0.0-rc.4","jsonwebtoken": "^8.5.1","vue": "^2.6.10","vue-router": "^3.0.3","vuex": "^3.0.1" },在server.js文件中配置跨域,并書寫測試接口:
let express = require('express') let bodyParser = require('body-parser')let app = express()// 配置跨域 app.use((req, res, next) => {res.header("Access-Control-Allow-Origin", "\*");res.header("Access-Control-Allow-Methods", "GET,HEAD,OPTIONS,POST,PUT"),res.header("Access-Control-Allow-Headers", "Origin,X-Requested-With,Content-Type,Accept,Authorization")if (req.method.toLowerCase() === "options") {return res.end();}next(); })// 配置bodyparser app.use(bodyParser.json())app.get("/user", (req, res) => {//在請求數據時,要加一個動畫,為了測試,所以讓它時間長點,加了一個定時器setTimeout(() => {res.json({name: "wangcai"})}, 500) })app.listen(3000)在router.js中配置路由:
routes: \[{path: '/',name: 'home',component: Home},{path: '/login',name: 'login',component: () => import('./views/Login.vue')},{path: '/profile',name: 'profile',component: () => import('./views/Profile.vue')} \]因為項目中需要用到樣式什么的,這里我引入了iView,main.js代碼:
import Vue from 'vue' import App from './App.vue' import router from './router' import store from './store'//引入iView import iView from 'iview' import 'iview/dist/styles/iview.css'; Vue.use(iView)Vue.config.productionTip = falsenew Vue({router,store,render: h => h(App) }).$mount('#app')因為項目中要用到加載數據的動畫,所以需要在store.js中的state中配置:
state: {//定義動畫是否顯示isShowLoading:false,username:'wangcai'},mutations: {//使動畫顯示showLoading(state){state.isShowLoading = true;},//使動畫隱藏hideLoading(state){state.isShowLoading = false;} },在App.vue中配置跳轉:
<template><div><div><router-link to="/">Home</router-link> |<router-link to="/login">Login</router-link> |<router-link to="/profile">Profile</router-link></div><router-view/></div> </template>Home.vue代碼:
<template><div><h1>首頁面</h1></div> </template>Login.vue代碼:
<template><div><i-input placeholder="請輸入用戶名..."></i-input><i-button type="primary">登錄</i-button></div> </template>Profile.vue代碼:
<template> <div><h1>個人中心</h1> </div> </template>然后在libs文件夾下面新建一個ajaxRequest.js文件,用來封裝我們自己的 axios 和 加載動畫 等
搜索公縱號:MarkerHub,關注回復[?vue?]獲取前后端入門教程!
import axios from 'axios' import store from '../store'//當第一次請求時,顯示loading class AjaxRequest {//當new的時候,調用這個方法constructor() {//請求的基礎路徑this.baseURL = process.env.NODE\_ENV == "production" ? "/" : "http://localhost:3000"this.timeout = 3000 //超時時間this.queue = {} //存放每一次的請求}//定義一個方法,把options展開merge(options) {return {...options,baseURL: this.baseURL,timeout: this.timeout}}//封裝一個攔截方法setInterceptor(instance, url) {//請求攔截,每次請求時,都要加上一個loading效果instance.interceptors.request.use((config) => {//每次請求時,都給他加一個Authorization頭,在JWT驗證時要用config.headers.Authorization = 'xxx'//第一次請求時,顯示loading動畫if (Object.keys(this.queue).length === 0) {store.commit('showLoading')}this.queue\[url\] = url;return config})//響應攔截instance.interceptors.response.use((res) => {//刪除queue里面的鏈接,如果同一個按鈕,你一秒之內點擊無數次,但是他只處理第一次操作delete this.queue\[url\]//隱藏loading動畫if (Object.keys(this.queue).length === 0) {store.commit('hideLoading')}//返回的結果return res.data})}request(options) {let instance = axios.create() //創建一個axios實例this.setInterceptor(instance, options.url) //設置攔截let config = this.merge(options)return instance(config) //axios執行后,返回promise} }export default new AjaxRequest;然后在api文件夾下新建一個user.js文件用來放用戶相關的調用接口的方法(當你想要調用接口的時候,直接調用里面的方法就好):
import axios from '../libs/ajaxRequset'// 用戶相關的接口 export const getUser = ()=>{return axios.request({url:'/user',method:'get'}) }修改Home.vue中的代碼:
<template><div><h1>首頁面</h1><p>當前登錄的用戶名是{{$store.state.username}}</p></div> </template><script> //如果用export導出的話,要用這種形式,相當于解構賦值 import {getUser} from '../api/user' export default {name:'home',async mounted(){let r = await getUser()console.log(r);} } </script>修改App.vue中的代碼(加動畫效果):
<template><div><Spin size="large" fix v-if="$store.state.isShowLoading">加載中...</Spin><div><router-link to="/">Home</router-link> |<router-link to="/login">Login</router-link> |<router-link to="/profile">Profile</router-link></div><router-view/></div> </template><style> #app {font-family: "Avenir", Helvetica, Arial, sans-serif;-webkit-font-smoothing: antialiased;-moz-osx-font-smoothing: grayscale;text-align: center;color: #2c3e50; } #nav {padding: 30px; }#nav a {font-weight: bold;color: #2c3e50; }#nav a.router-link-exact-active {color: #42b983; } </style>運行效果:
使用 JWT
在server.js中新增代碼,使用 JWT,并寫好登錄和驗證的接口:
let jwt = require('jsonwebtoken')let secret = "xwc"//登錄的接口 app.post('/login',(req,res)=>{let {username} = req.body;if(username === 'Fan'){//登錄成功后返回一個tokenres.json({code:0,username:'Fan',token:jwt.sign({username:'Fan'},secret,{expiresIn:20 //表示token20秒過期})})}else{//登錄失敗res.json({code:1,data:'登錄失敗了'})} })//驗證token的接口 app.get('/validate',(req,res)=>{let token = req.headers.authorization; //我們會把token放到我們自己設置的http的頭authorization中,在這里可以直接拿到jwt.verify(token,secret,(err,decode)=>{ //驗證tokenif(err){return res.json({code:1,data:'token失效了'})}else{// token合法 在這里,需要把token的時效延長,//總不能我們看著看著突然讓我們重新登錄,token過期的意思是,你在這之間不進行任何操作才會過期res.json({code:0,username:decode.username,token:jwt.sign({username:'Fan'},secret,{ //合法時,我們需要重新生成一個token,我們每次切換路由,都要重新生成一個tokenexpiresIn:20})})}}) })接著在api文件夾下的user.js文件中添加登錄和驗證的方法:
import axios from '../libs/ajaxRequest'// 用戶相關的接口// 調獲取用戶信息的接口 向外暴露一個getUser方法 這個方法中調了接口 // 在組件中,就可以使用getUser,就相當于調用接口 export const getUser = ()=>{return axios.request({url:'/user',method:'get'}) }// 再向外暴露一個登錄的方法,方法內部也是調接口 // 在登錄組件中就可以調用Login方法,需要給方法傳遞一個用戶名 export const login = (username)=>{return axios.request({url:'/login',method:'post',data:{username}}) }//驗證token方法 export const validate = ()=>{return axios.request({url:'/validate',method:'get'}) }接著我們在lib文件夾下新建一個local.js文件,用來設置或者獲取localStorage里的token:
//把獲得到的token存到localStorage里 export const setLocal = (key,value)=>{if(typeof value == 'object'){ //如果傳過來的是對象,則轉換成字符串value = JSON.stringify(value)}localStorage.setItem(key,value) //存到localStorage里 }//獲取localStorage里的token export const getLocal = (key)=>{return localStorage.getItem(key) }然后修改store.js中的代碼:
import Vue from 'vue' import Vuex from 'vuex' import {login,validate} from './api/user' //必須用這種方式引入 import {setLocal} from './libs/local' //引入lib文件夾下的local.js文件中的setLocal方法(往localStorage里存放token)Vue.use(Vuex)export default new Vuex.Store({state: {//定義動畫是否顯示isShowLoading:false,username:'wangcai'},mutations: {//使動畫顯示showLoading(state){state.isShowLoading = true;},//使動畫隱藏hideLoading(state){state.isShowLoading = false;},//定義修改用戶名的方法setUser(state,username){state.username = username}},// actions存放接口的調用 dispatch actions里面放方法actions: {//這里面所有的方法都是異步的//登錄方法async toLogin({commit},username){let r = await login(username) //調用user.js中的login方法,也就是調用登錄接口// console.log(r);if(r.code === 0){ //登錄成功后會給你返回json數據,里面有code//登錄成功了commit('setUser',r.username) //修改用戶名setLocal('token',r.token) //把得到的token存到localStorage里}else{// console.log('............');return Promise.reject(r.data); //如果失敗,返回一個promise失敗態}},//驗證token方法async validate({commit}){let r = await validate(); //調用user.js中的validate方法,也就是調用驗證接口if(r.code === 0){commit('setUser',r.username)setLocal('token',r.token) //我們說了,驗證通過,或者每次切換路由時,都要重新生成token}return r.code === 0; //返回token是否失效,true或者false}} })修改Login.vue中的代碼:
<template><div><i-input v-model="username" placeholder="請輸入用戶名..."></i-input><i-button type="primary" @click="login()">登錄</i-button></div> </template><script> import {mapActions} from 'vuex' //使用vuex中的mapActions方法,不會的請參考我的文章vuex的使用方法 export default {data(){return{username:'' //定義一個用戶名}},methods:{...mapActions(\['toLogin'\]), //獲取store.js文件中的actions中的toLogin方法login(){// console.log(this\['toLogin'\](this.username));//使用獲取到的toLogin方法this\['toLogin'\](this.username).then(data=>{ //因為toLogin返回的是一個Promise,所以可以.thenthis.$router.push('/') //登錄成功,跳到首頁面},err=>{this.$Message.error(err)})}} } </script>別忘了修改ajaxRequest.js文件,在請求攔截的時候,需要加個頭,前面我們寫死了,這里,要把 token 給他,然后每次路由跳轉訪問頁面的時候,都會帶上這個頭,用來驗證:
import {getLocal} from "../libs/local" //引入//請求攔截,每次請求時,都要加上一個loading效果 instance.interceptors.request.use((config) => {//每次請求時,都給他加一個Authorization頭,在JWT驗證時要用config.headers.Authorization = getLocal('token')//第一次請求時,顯示loading動畫if (Object.keys(this.queue).length === 0) {store.commit('showLoading')}this.queue\[url\] = url;return config })接著在router.js中設置路由:
哪個頁面需要登錄后才能訪問的話,給這個路由添加meta,假如我的 個人中心頁面 需要登錄后才能訪問,那么我需要修改代碼:
最后修改main.js中的代碼,當切換路由時,進行驗證:
import Vue from 'vue' import App from './App.vue' import router from './router' import store from './store'//引入iView import iView from 'iview' import 'iview/dist/styles/iview.css';Vue.use(iView)Vue.config.productionTip = false//每一次切換路由時,都執行這個導航守衛 router.beforeEach(async (to,from,next)=>{let isLogin = await store.dispatch('validate') //判斷是否登錄了// needLogin 表示哪些路由需要在登錄條件下才能訪問console.log(to);let needLogin = to.matched.some(match=>match.meta.needLogin)if(needLogin){//需要登錄if(isLogin){//登錄過了next()}else{//沒有登錄next('/login')}}else{//不需要登錄if(isLogin && to.path === '/login'){ //如果你訪問login頁面,則給你跳到首頁面,因為不需要登錄next('/')}else{next()}} })new Vue({router,store,render: h => h(App) }).$mount('#app')有關需要注意的點,都加注釋了,好好看注釋就行
至此,整個案例就結束了
總結
以上是生活随笔為你收集整理的Vue项目使用拦截器和JWT验证 完整案例的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 网络编程-libevnet不带缓冲的事件
- 下一篇: java module和model_Ja