总体思路
- 使用vue的状态管理模式(vuex)结合路由导航守卫来实现
具体实现代码分析
- 在登录页面(src/login/index.vue)点击登录,触发handleLogin方法
handleLogin() {
//表单验证后触发里面的回调
this.$refs.loginForm.validate(valid => {
//表单验证成功
if (valid) {
this.loading = true
//将表单提交到vuex中的user模块中的action中的login方法 src/store/module/user.js
//实际这里并没有向服务器发送登录请求、登录请求是在src/store/modules/user.js中的action里面发送的
//等待vuex中的登录操作完成后返回的Promise状态改变后 如果登录成功执行then中的代码
this.$store.dispatch('user/login', this.loginForm).then(() => {
//登录成功触发路由跳转 由此转入到@/premission.js中的router.beforeEach()路由守卫中执行里面的代码
this.$router.push({ path: this.redirect || '/' })
this.loading = false
}).catch(() => {
this.loading = false
})
} else {
//表单验证失败
console.log('error submit!!')
return false
}
})
}
-
store中的modules里的user.js代码如下
import { login, logout, getInfo } from '@/api/user' import { getToken, setToken, removeToken } from '@/utils/auth' import router, { resetRouter } from '@/router' const state = { token: getToken(), name: '', avatar: '', introduction: '', roles: [] } const mutations = { SET_TOKEN: (state, token) => { state.token = token }, SET_INTRODUCTION: (state, introduction) => { state.introduction = introduction }, SET_NAME: (state, name) => { state.name = name }, SET_AVATAR: (state, avatar) => { state.avatar = avatar }, SET_ROLES: (state, roles) => { state.roles = roles } } const actions = { //处理用户登录 login({ commit }, userInfo) { const { username, password } = userInfo return new Promise((resolve, reject) => { //向后台发送登录请求 login({ username: username.trim(), password: password }).then(response => { const { data } = response //拿到token后将token保存在vuex中 commit('SET_TOKEN', data.token) //将token保存在cookie中 setToken(data.token) //执行src/login/index.vue中提交状态改变代码的地方的then中的代码 resolve() }).catch(error => { reject(error) }) }) }, // get user info getInfo({ commit, state }) { return new Promise((resolve, reject) => { getInfo(state.token).then(response => { const { data } = response if (!data) { reject('Verification failed, please Login again.') } const { roles, name, avatar, introduction } = data // roles must be a non-empty array if (!roles || roles.length <= 0) { reject('getInfo: roles must be a non-null array!') } commit('SET_ROLES', roles) commit('SET_NAME', name) commit('SET_AVATAR', avatar) commit('SET_INTRODUCTION', introduction) resolve(data) }).catch(error => { reject(error) }) }) }, // user logout logout({ commit, state, dispatch }) { return new Promise((resolve, reject) => { logout(state.token).then(() => { commit('SET_TOKEN', '') commit('SET_ROLES', []) removeToken() resetRouter() // reset visited views and cached views // to fixed https://github.com/PanJiaChen/vue-element-admin/issues/2485 dispatch('tagsView/delAllViews', null, { root: true }) resolve() }).catch(error => { reject(error) }) }) }, // remove token resetToken({ commit }) { return new Promise(resolve => { commit('SET_TOKEN', '') commit('SET_ROLES', []) removeToken() resolve() }) }, // dynamically modify permissions async changeRoles({ commit, dispatch }, role) { const token = role + '-token' commit('SET_TOKEN', token) setToken(token) const { roles } = await dispatch('getInfo') resetRouter() // generate accessible routes map based on roles const accessRoutes = await dispatch('permission/generateRoutes', roles, { root: true }) // dynamically add accessible routes router.addRoutes(accessRoutes) // reset visited views and cached views dispatch('tagsView/delAllViews', null, { root: true }) } } export default { namespaced: true, state, mutations, actions }-
3.main.js中的引入的permission.js这个文件是关键。里面代码使用路由守卫结合vuex实现了根据不同的角色生成不同的可访问路由的功能,也就是实现了权限管理。就好像你放在每个页面的一条看门狗
-
import router from './router'
import store from './store'
import { Message } from 'element-ui'
import NProgress from 'nprogress' // progress bar
import 'nprogress/nprogress.css' // progress bar style
import { getToken } from '@/utils/auth' // get token from cookie
import getPageTitle from '@/utils/get-page-title'
NProgress.configure({ showSpinner: false }) // NProgress Configuration
const whiteList = ['/login', '/auth-redirect'] // no redirect whitelist
//每次路由跳转时都要走这里
router.beforeEach(async(to, from, next) => {
// start progress bar
NProgress.start()
// 设置页面title
document.title = getPageTitle(to.meta.title)
// determine whether the user has logged in
const hasToken = getToken()
if (hasToken) {
if (to.path === '/login') {
// if is logged in, redirect to the home page
next({ path: '/' })
NProgress.done() // hack: https://github.com/PanJiaChen/vue-element-admin/pull/2939
} else {
// determine whether the user has obtained his permission roles through getInfo
const hasRoles = store.getters.roles && store.getters.roles.length > 0
//在vuex中获取roles 如果是第一次登录 这个时候hasRoles为false
if (hasRoles) {
next()
} else {
try {
// get user info
// note: roles must be a object array! such as: ['admin'] or ,['developer','editor']
// 在vuex中向后端发送获取角色信息的请求 并返回角色
const { roles } = await store.dispatch('user/getInfo')
//** 生成该角色能访问的路由 **
//生成路由表的工作由src/store/modules/permission.js完成
const accessRoutes = await store.dispatch('permission/generateRoutes', roles)
console.log(accessRoutes)
// dynamically add accessible routes
// hack method to ensure that addRoutes is complete
// set the replace: true, so the navigation will not leave a history record
next({ ...to, replace: true })
} catch (error) {
// remove token and go to login page to re-login
await store.dispatch('user/resetToken')
Message.error(error || 'Has Error')
next(`/login?redirect=${to.path}`)
NProgress.done()
}
}
}
} else {
/* has no token*/
if (whiteList.indexOf(to.path) !== -1) {
// in the free login whitelist, go directly
next()
} else {
// other pages that do not have permission to access are redirected to the login page.
next(`/login?redirect=${to.path}`)
NProgress.done()
}
}
})
router.afterEach(() => {
// finish progress bar
NProgress.done()
})
- 动态生成路由的具体代码 src/store/modules/permission.js
import { asyncRoutes, constantRoutes } from '@/router'
/**
* Use meta.role to determine if the current user has permission
* @param roles
* @param route
*/
function hasPermission(roles, route) {
if (route.meta && route.meta.roles) {
return roles.some(role => route.meta.roles.includes(role))
} else {
return true
}
}
/**
* Filter asynchronous routing tables by recursion
* 使用递归过滤异步路由(实现不同的角色不同的路由)
* @param routes asyncRoutes
* @param roles
*/
export function filterAsyncRoutes(routes, roles) {
const res = []
routes.forEach(route => {
const tmp = { ...route }
if (hasPermission(roles, tmp)) {
if (tmp.children) {
tmp.children = filterAsyncRoutes(tmp.children, roles)
}
res.push(tmp)
}
})
return res
}
const state = {
routes: [],
addRoutes: []
}
const mutations = {
SET_ROUTES: (state, routes) => {
state.addRoutes = routes
state.routes = constantRoutes.concat(routes)
}
}
const actions = {
generateRoutes({ commit }, roles) {
return new Promise(resolve => {
let accessedRoutes
if (roles.includes('admin')) {
//admin拥有所有路由权限
accessedRoutes = asyncRoutes || []
} else {
//从异步路由中根据不同的角色递归的过滤出该角色能访问的路由
accessedRoutes = filterAsyncRoutes(asyncRoutes, roles)
}
commit('SET_ROUTES', accessedRoutes)
//将生成的有权限的路由返回给调用该方法处的then 流程走完
resolve(accessedRoutes)
})
}
}