几乎在全部的项目中都离不开拦截器和登陆验证,这是必需的。若是你学会了这个demo,那么几乎全部网站的登陆验证,加载动画就都会了,因此背也要背会css
因此本章以一个demo为例,来帮助你们理解拦截器和登陆验证控制vue
文章后面有源码,能够下载下来运行一下ios
先来看看效果:git
功能:web
当你访问首页的时候,会有一个加载动画,就是拦截器的功劳,而且首页会有一个当前登陆的用户名,默认是wangcai,等你登陆成功后,会替换成你本身登陆的用户名ajax
当你没有登陆的时候,能够访问首页和登陆页,可是访问不了我的中心(Profile),当你访问我的中心,会给你自动跳转到登陆页vue-router
当你在登陆页进行登陆,若是用户名输入错误的话,会弹出错误信息vuex
当你输入正确的时候(我设置了Fan为正确的用户名),点击登陆,登陆成功后,会自动给你跳转到首页express
而且登陆成功后,若是你再点击想进入登陆页,是不行的,他会自动给你跳转到首页json
登陆成功后,就能够访问 我的中心页面
若是你超过 20秒 不对页面进行操做(我设置的是20秒,能够自行设置),那么token会自动失效,那么你就访问不了我的中心,你须要再次登陆
若是你在 20秒 以内,操做页面的话,那么token的值是不会失效的,因此是不须要再次登陆的。也就是说,在 20秒 以内,你每次进行路由跳转的时候,token的值和时间就会自动重置,防止失效让你再次登陆(总不能让你看着看着忽然让你登陆)
下面就让咱们开始吧!!! (有关代码的解释说明已在代码中注释)
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 = false
new 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 id="app">
<div id="nav">
<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 class="home">
<h1>首页面</h1>
</div>
</template>
复制代码
Login.vue
代码:
<template>
<div>
<i-input placeholder="请输入用户名..." style="width: 300px"></i-input>
<i-button type="primary">登陆</i-button>
</div>
</template>
复制代码
Profile.vue
代码:
<template>
<div>
<h1>我的中心</h1>
</div>
</template>
复制代码
而后在libs
文件夹下面新建一个ajaxRequest.js
文件,用来封装咱们本身的 axios 和 加载动画 等
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 class="home">
<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 id="app">
<Spin size="large" fix v-if="$store.state.isShowLoading">
加载中...
</Spin>
<div id="nav">
<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>
复制代码
运行效果:
在server.js
中新增代码,使用JWT,并写好登陆和验证的接口:
let jwt = require('jsonwebtoken')
let secret = "xwc"
//登陆的接口
app.post('/login',(req,res)=>{
let {username} = req.body;
if(username === 'Fan'){
//登陆成功后返回一个token
res.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)=>{ //验证token
if(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,咱们每次切换路由,都要从新生成一个token
expiresIn: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="请输入用户名..." style="width: 300px"></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,因此能够.then
this.$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
,假如个人 我的中心页面 须要登陆后才能访问,那么我须要修改代码:
{
path: '/profile',
name: 'profile',
component: () => import('./views/Profile.vue'),
meta:{
needLogin:true
}
}
复制代码
最后修改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')
复制代码
有关须要注意的点,都加注释了,好好看注释就行
至此,整个案例就结束了
点我获取源码