Vue中的登录查看权限控制
参考:
【1】vue项目将token存在(vuex)store和中
【2】关于后端的权限控制(文章第三部分)
基本根据上面的参考修改。
主要添加的有这几部分:
1. 安装vuex使用存储功能
【引用】
cnpm install vuex --save
在前后端完全分离的情况下,Vue项目中实现token验证大致思路如下:
第一次登录的时候,前端调后端的登陆接口,发送电话号码(phone)和密码()后端收到请求,验证phone和,验证成功imToken官网下载,就给前端返回一个token前端拿到token,将token存储到和vuex中,并跳转路由首页index前端每次跳转路由token 权限管理·(中国)官方网站,就判断 中有无 token ,没有就跳转到登录页面,有则跳转到对应路由页面每次调后端接口,都要在请求头中加token后端判断请求头中有无token,有token,就拿到token并验证token,验证成功就返回数据,验证失败(例如:token过期)就返回400,请求头中没有token也返回400(参考【2】)如果前端拿到状态码为400,就清除token信息并跳转到登录页面 2. model/.js
相当于一个方法,里面定义了一些对存储的信息进行的操作,不写这个方法直接写在store/index.js中也可以。
在这里实现了几个方法:
设置所储存的key和value通过key获得value移除key对应的value移除所有暂存信息获得所有暂存信息
var storage = {
set (key, value) {
localStorage.setItem(key, JSON.stringify(value))
},
get (key) {
return JSON.parse(localStorage.getItem(key))
},
remove (key) {
localStorage.removeItem(key)
},
removeAll () {
localStorage.clear()
},
getAll () {
let len = localStorage.length
// eslint-disable-next-line no-array-constructor
let arr = []
for (var i = 0; i < len; i++) {
var getKey = localStorage.key(i)
var getVal = localStorage.getItem(getKey)
arr[i].push({
'key': getKey,
'val': getVal
})
}
return arr
}
}
export default storage
3. store/index.js
相当于具体的过程。
中写的方法可以通过mit(方法名, 具体数值)来进行调用,比如要调用(state, token):_this.$mit('', token)
import Vue from 'vue'
import Vuex from 'vuex'
import storage from '../model/storage'
Vue.use(Vuex)
// 用Vuex.Store对象用来记录token
// 存储需要的信息
const store = new Vuex.Store({
state: {
token: '',
info: {
phone: '',
job: '',
id: ''
}
},
// 计算属性
mutations: {
// 修改token,并将token存入localStorage
set_token (state, token) {
state.token = token
storage.set('token', token)
console.log('store、localstorage保存token成功!')
},
del_token (state) {
state.token = ''
storage.remove('token')
},
// 可选
setUserInfo (state, userName) {
state.userName = userName
}
},
actions: {
// removeToken: (context) => {
// context.commit('set_token')
// }
}
})
export default store
4. utils/.js和/index.js
.js相当于重写了一个方法,在使用axios方法时,直接用重写的即可。同时定义了拦截器的检查,如果后台返回的是400的话(自己在后台定义具体状态码),即未登录/token失效。
import axios from 'axios'
import store from '@/store'
import router from '@/router'
// create an axios instance
const service = axios.create({
baseURL: '/api', // url = base url + request url
timeout: 5000 // request timeout
})
// 添加请求拦截器,若token存在则在请求头中加token,不存在也继续请求
service.interceptors.request.use(
config => {
// 每次发送请求之前检测都vuex存有token,那么都要放在请求头发送给服务器,没有则不带token
// Authorization是必须的
if (store.state.token) {
config.headers.Authorization = store.getters.get_token
}
return config
},
error => {
console.log('在request拦截器显示错误:', error.response)
return Promise.reject(error)
}
)
// respone拦截器
service.interceptors.response.use(
response => {
// 在status正确的情况下,code不正确则返回对应的错误信息(后台自定义为200是正确,并且将错误信息写在message),正确则返回响应
return response.data.status === 200 ? response : Promise.reject(response.data.message)
},
error => {
// 在status不正确的情况下,判别status状态码给出对应响应
if (error.response) {
console.log('在respone拦截器显示错误:', error.response)
switch (error.response.status) {
case 401:
store.commit('del_token')
router.replace({
// 跳转到登录页面
path: '/login',
// 将跳转的路由path作为参数,登录成功后跳转到该路由
query: { redirect: router.currentRoute.fullPath }
})
break
case 400:
store.commit('del_token')
alert('请先登录')
router.replace({
// 跳转到登录页面
path: '/login',
// 将跳转的路由path作为参数,登录成功后跳转到该路由,方便登陆成功后再跳转到刚刚的页面
query: { redirect: router.currentRoute.fullPath }
})
break
}
}
return Promise.reject(error.response.data)
}
)
export default service
把这两部分放在一起讲是因为,/index.js定义了跳转路由的域名,使得能够跨域访问,并方便调用,即调用"/api"就是调用中定义的,仅修改即可:
proxyTable: {
'/api': {
target: 'http://localhost:8090',
changeOrigin: true,
pathRewrite: {
'^/api': ''
}
}
},
5. /index.js
检查每个页面跳转时token是否失效。如果失效的话重新登陆。
import Vue from 'vue'
import Router from 'vue-router'
import Login from '../pages/login.vue'
import Index from '../pages/index.vue'
import store from '../store'
Vue.use(Router)
const routes = [
{
path: '/',
name: 'Login',
component: Login
},
{
path: '/login',
name: 'Login',
component: Login
},
{
path: '/index',
name: 'Index',
component: Index,
meta: {
requireAuth: true
}
}
]
const router = new Router({ routes: routes })
router.beforeEach((to, from, next) => {
// 判断要去的路由有没有requiresAuth
// to.matched.some(r => r.meta.requireAuth) or to.meta.requiresAuth
if (to.matched.some(r => r.meta.requireAuth)) {
if (store.state.token) {
// 有token,进行request请求,后台还会验证token
next()
} else {
next({
path: '/login',
// 将刚刚要去的路由path(却无权限)作为参数,方便登录成功后直接跳转到该路由,这要进一步在登陆页面判断
query: { redirect: to.fullPath }
})
}
} else {
next()
}
})
export default router
6. 具体应用
首先在main.js中设置全局store:
import Vue from 'vue'
import App from './App'
import 'ant-design-vue/dist/antd.css'
import router from './router'
import Antd from 'ant-design-vue'
import axios from 'axios'
import VueAxios from 'vue-axios'
import store from './store'
Vue.use(VueAxios, axios)
Vue.config.productionTip = false
Vue.use(Antd)
/* eslint-disable no-new */
new Vue({
el: '#app',
router,
store,
components: { App },
template: ' '
})
把this重新定义为_this的原因:
import request from '../utils/request.js'
export default {
name: 'LoginForm',
data () {
return {
form: this.$form.createForm(this, { name: 'login_form' })
}
},
methods: {
login (e) {
e.preventDefault()
let _this = this
this.form.validateFields((err, values) => {
if (!err) {
request({
url: '/login',
method: 'post',
params: {
phone: values.phone,
password: values.password
}
})
.then(function (response) {
console.log(response)
if (response.status === 200) {
const token = response.data.token
_this.$store.commit('set_token', token)
console.log(response.data)
_this.$router.push('index')
} else {
// alert(response.data.data.message)
}
}).catch(function (error) {
console.log(error)
})
}
})
}
}
}
7. 问题:刷新页面以后store中的数据会丢失,重新返回登录页面
解决方案:使用存储token数据
需要修改app.vue
<script>
export default {
name: 'App',
data () {
return {
imgUrl: require('./assets/icon_logo.png')
}
},
created () {
if (sessionStorage.getItem('store')) {
let state = JSON.parse(sessionStorage.getItem('store'))
this.$store.commit('set_token', state.token)
this.$store.commit('setUserInfo', state.info)
sessionStorage.setItem('store', '')
}
window.onbeforeunload = this.beforeunloadFn
},
methods: {
beforeunloadFn (e) {
// 这个事件只有在鼠标真正和浏览器有了交互,再刷新或者关闭时才会触发, 浏览器事件会弹框确认用户是否要离开页面
e = e || window.event
if (e) {
sessionStorage.setItem('store', JSON.stringify(this.$store.state))
}
}
}
}
</script>
由于在跳转路由时就需要验证是否有token,所以/index.js中也需要要修改对于有无token的验证:
实际只增加了对中储存的token的验证
router.beforeEach((to, from, next) => {
// 判断要去的路由有没有requiresAuth
// to.matched.some(r => r.meta.requireAuth) or to.meta.requiresAuth
if (to.matched.some(r => r.meta.requireAuth)) {
if (store.state.token || JSON.parse(sessionStorage.getItem('store')).token) {
// 有token,进行request请求,后台还会验证token
console.log('有token')
next()
} else {
next({
path: '/login',
// 将刚刚要去的路由path(却无权限)作为参数,方便登录成功后直接跳转到该路由,这要进一步在登陆页面判断
query: { redirect: to.fullPath }
})
}
} else {
next()
}
})
发表评论:
◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。