因为 Vuex 中保存的用户信息,在每次页面刷新的时候,都会丢失。
而且,在每次用户切换页面的时候,都要去判断用户是否登录。
所以,在每次页面切换的时候,都去请求一个后端接口,去校验用户是否登录。
如果已经登录,那么这个接口,会返回 username ,并且返回一个新的 JWT Token,然后再存到客户端,用来延长 Token 的有效时长。
1. 首先在 server/server.js 中,编写一个校验用户是否登录的接口
server.js :
// 模拟后端,提供接口
let express = require('express')
let app = express()
let bodyParser = require('body-parser')
let jwt = require('jsonwebtoken') // 引入 JWT的包
// 处理跨域
app.use((req, res, next) => {
res.header('Access-Control-Allow-Origin', 'http://localhost:8080')
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, X-Auth-Token'
)
// 如果是options 请求,那么不理它
if (req.method.toLowerCase() === 'options') {
return res.end()
}
next()
})
app.use(bodyParser.json())
// 设置一个 JWT Token 的密钥, 用来给 JWT Token生成的字符串加密
let secret = 'zf'
app.get('/user', (req, res) => {
res.json({ name: 'zf' })
})
// 登录接口
app.post('/login', (req, res) => {
let { username, password } = req.body // 请求体里面的数据,在body里面取的
// 登录成功
if (username === 'admin' && password === '123456') {
// 登录成功
res.json({
code: 0,
username: 'admin',
// 给客户端返回一个Token
auth_token: jwt.sign({ username: 'admin' }, secret, {
expiresIn: 120 // 过期时间 120S, 超过120S失效,即2分钟失效
})
})
} else {
res.json({
code: 1,
data: '用户名或者密码错误'
})
}
})
// 判断用户是否登录
app.get('/validate', (req, res) => {
console.log(req.headers)
// 判断请求头上,是否有 JWT Token
// let token = req.headers['X-Auth-Token']
let token = req.headers['x-auth-token'] // 取请求头中的x-auth-token时,必须小写。
console.log('tt')
// 没有取到请求头中的 X-Auth-Token
console.log(token)
// 使用jwt包提供的方法 verify() 来校验 JWT Token
// decode 就等于之前存的 {username: 'admin'} 对象
// JWT Token验证成功后,就可以把{username: 'admin'} 这个对象,返回给客户端
jwt.verify(token, secret, (err, decode) => {
if (err) {
// err表示已过期
return res.json({
code: 1,
data: 'token已失效'
})
} else {
res.json({
// 表示用户已登录,并重新把用户名,返回给客户端
username: decode.username,
code: 0,
// 是用户操作期间,需要把Token的时效延长
// 因为beforeEach方法,在用户每切换一次页面的时候,都会调用。
// 所以,每一次用户切换页面的时候,都会请求这个方法。
// 所以,每请求这个方法,就把Token的过期时间,重新设置一下。
// 所以,需要再给用户传回去一个新的JWT Token,
// 然后,用户在客户端,再把新的JWT Token存一下。
auth_token: jwt.sign({ username: 'admin' }, secret, {
expiresIn: 120 // 过期时间 120S, 超过120S失效,即2分钟失效
})
})
}
})
})
app.listen(3000)
2. 在 api/user.js 新建一个调取接口的方法
user.js:
// 用户登录接口
// 专门写,调取后端的接口
import axios from '../utils/ajaxRequest'
export const getUser = () => {
// return 以后,在其他组件,调用: getUser().then()
// import { getUser } from './api/user'
// getUser().then(data => {})
return axios.request({
url: '/user',
method: 'get'
})
}
// 登录方法
// 传入用户名和密码
export const login = (username, password) => {
return axios.request({
method: 'POST',
url: '/login',
data: {
username,
password
}
})
}
// 在main.js 中的beforeEach中,去校验用户是否登录
export const validate = () => {
return axios.request({
method: 'get',
url: '/validate'
})
}
3. 然后在 actions 中,去调取 api/user.js 中的方法
store/index.js:
import Vue from 'vue'
import Vuex from 'vuex'
import { login, validate } from '../api/user'
// import * as types from './mutation-types'
import { setLocal } from '../utils/local' // 将JWT Token 存入 localStorage
Vue.use(Vuex)
export default new Vuex.Store({
state: {
isShowLoading: false, // 是否显示loading效果
username: 'x' // 登录后的用户名
},
mutations: {
showLoading (state) {
state.isShowLoading = true // 显示loading
},
hideLoading (state) {
state.isShowLoading = false // 隐藏loading
},
setUser (state, username) {
state.username = username // 更改公共状态
}
},
// 在actions中,去调取 api/user.js 中的方法
// 然后,在组件中,去触发 actions 中的方法
actions: {
// 一般在actions中,写接口调用
async doLogin ({ commit }, payload) {
console.log(payload.username)
console.log(payload.password)
// store.dispatch('doLogin')
let res = await login(payload.username, payload.password)
// 登录成功
if (res.code === 0) {
// 把用户名存入 Vuex
commit('setUser', res.username)
// 将返回客户端的JWT Token保存到客户端上,每次请求时,请求头上加上Token,
// 然后服务端校验 Token, 如果Token不正确,或者过期,则相当于没有登录
setLocal('auth_token', res.auth_token)
} else {
// 返回的失败的promise
return Promise.reject(res.data)
}
},
// 在 main.js 中的beforeEach 去校验用户是否登录
async doValidate ({ commit }) {
let res = await validate()
// 表示已登录过
if (res.code === 0) {
// 再给username设置一次值
commit('setUser', res.username)
// 再把校验后的Token,再存入客户端一次,从而延长时效。
setLocal('auth_token', res.auth_token)
}
return res.code === 0 // 返回用户是否已登录, true表示已登录, false表示用户失效
}
}
})
4. 在 main.js 中的beforeEach方法中,去校验用户是否登录
因为,页面每次切换,都会调用 beforeEach 方法,所以,每次页面切换的时候,都会去校验用户是否登录。
main.js :
import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store/index'
// 使用 ant design vue
import Antd from 'ant-design-vue'
import 'ant-design-vue/dist/antd.css'
// 全局插件
import vueComponent from './components/vue-component.js'
Vue.config.productionTip = false
// 全局注册 Antd
Vue.use(Antd)
Vue.use(vueComponent)
// 做一个白名单,哪些路径,不需要进行用户登录的校验
let whiteList = ['/order_details_tabs']
// beforeEach,对用户是否登录,进行校验
router.beforeEach(async (to, from, next) => {
// 如果在白名单,那么就不需要经过用户登录的校验
if (whiteList.includes(to.path)) {
return next()
}
// 在服务端,有一个接口,去判断用户是否登录
// 去请求后台接口,判断用户是否登录
let isLogin = await store.dispatch('doValidate')
console.log(isLogin)
// 如果这个页面需要先登录
let needLogin = to.matched.some(match => match.meta.needLogin)
if (needLogin) {
// 如果需要登录
// 并且已经登录,就接着往下走
if (isLogin) {
next()
} else {
// 没有登录
next('/login') // 就跳转到login页面
}
} else {
// 否则
// 如果已经登录了,并且访问的是 'login'的时候,就跳转到首页
if (isLogin && to.name === 'login') {
next('/')
} else {
next()
}
}
next()
})
new Vue({
router,
store,
render: h => h(App)
}).$mount('#app')
5. 注意:
必须在 server/server.js 中这样取请求头中的 'x-auth-token'
必须小写。