Vue2.0 + ElementUI 手写权限管理系统后台模板(二)——权限管理
权限验证
页面级别权限
路由:
默认挂载不需要权限的路由,例如:登录、主页。需要权限的页面通过 router.addRoutes(点击查看官方文档) 动态添加更多的路由规则,404拦截页面需要放在路由表的最后,否则 /404 后面的路由会被404拦截,通过路由元信息meta(点击查看官方文档)记录路由需要的权限。为了菜单列表可以被翻译,路由表的 name 属性值通过 i18n 的英文对照表来获取,也可以直接写英文名称,如 name: routeNmae.builtInIcon
可以直接写成 name: "builtInIcon"
,凭个人喜好
// src/router/index.js
import en from '../i18n/lang/en' // 英文对照表
import Vue from 'vue'
import Router from 'vue-router'
import CommerViews from '@/views/commerViews'
import Login from '@/views/login/index'
import Layout from '@/views/layout/layout'
import HomeMain from '@/views/index/mainIndex'
// 不是必须加载的组件使用懒加载
const Icon = () => import('@/views/icon/index')
const Upload = () => import('@/views/upload/upload')
const Markdown = () => import('@/views/markdown/markdownView')
const NotFound = () => import('@/page404')
Vue.use(Router)
let routeNmae = en.routeNmae // 从英文翻译对照表获取路由的英文名字,当做路由'name'属性的值
// 不需要权限的路由
let defaultRouter = [
{ path: '/',
redirect: '/index',
hidden: true,
children: []
},
{
path: '/login',
component: Login,
name: '',
hidden: true,
children: []
},
{
path: '/index',
iconCls: 'fa fa-dashboard', // 菜单图标,直接填写字体图标的 class
name: routeNmae.home,
component: Layout,
alone: true,
children: [
{
path: '/index',
iconCls: 'fa fa-dashboard',
name: '主页',
component: HomeMain,
children: []
}
]
},
{
path: '/404',
component: NotFound,
name: '404',
hidden: true,
children: []
},
]
// 需要 addRouters 动态加载的路由
let addRouter = [
{
path: '/',
iconCls: 'fa fa-server',
name: routeNmae.multiDirectory,
component: Layout,
children: [
{
path: '/erji1',
iconCls: 'fa fa-server',
name: routeNmae['menu2-1'],
component: Erji,
children: []
},
{
path: '/erji3',
iconCls: 'fa fa-server',
name: routeNmae['menu2-3'],
component: CommerViews, // 无限极菜单的容器 超过三级菜单父级容器需要使用 CommerViews
children: [
{
path: '/sanji2',
iconCls: 'fa fa-server',
name: routeNmae['menu3-2'],
component: Sanji2,
children: []
},
{
path: '/sanji3',
iconCls: 'fa fa-server',
name: routeNmae['menu3-3'],
component: CommerViews,
children: [
{
path: '/siji',
iconCls: 'fa fa-server',
name: routeNmae['menu4-1'],
component: Siji,
children: []
},
{
path: '/siji1',
iconCls: 'fa fa-server',
name: routeNmae['menu4-2'],
component: CommerViews,
children: [
{
path: '/wuji',
iconCls: 'fa fa-server',
name: routeNmae['menu5-1'],
component: Wuji,
children: []
}
]
}
]
}
]
}
]
},
{
path: '/',
iconCls: 'el-icon-edit', // 图标样式class
name: routeNmae.editor,
component: Layout,
meta: {role: ['superAdmin', 'admin']}, // 需要权限 'superAdmin', 'admin'。meta属性可以放在父级,验证父级和所有子菜单,也可以放在子级单独验证某一个子菜单
children: [
{
path: '/markdown',
iconCls: 'fa fa-file-code-o', // 图标样式class
name: routeNmae.markdown,
component: Markdown,
children: []
}
]
},
{ path: '*', // 通配符拦截放在最后,不存在的路由全都指向404页面
redirect: '/404',
hidden: true,
children: []
},
]
export default new Router({
routes: defaultRouter
})
export {defaultRouter, addRouter}
然后通过 token
获取当前登录用户的个人信息,在router
被挂载到Vue之前和需要权限的路由表做对比,筛选出当前角色有权限访问的动态路由表,
// src/router/permission.js
// 获取角色信息,根据用户权限动态加载路由
router.beforeEach((to, from, next) => {
if (store.getters.token) { // 查看 token 是否存在
if (to.path === '/login') {
next({path: '/'})
} else {
if (!store.getters.info.role) { // 查看是否有当前用户角色,如果没有则获取角色信息
!async function getAddRouters () {
// 省略 axios 请求代码 通过 token 向后台请求用户权限等信息,这里用假数据赋值
await store.dispatch("getInfo", {
role: "superAdmin",
permissions: "超级管理员"
})
await store.dispatch("newRoutes", store.getters.info.role) // 通过权限筛选新路由表
let newAddRouters = store.getters.addRouters
await router.addRoutes(newAddRouters) // 动态加载新路由表
next({path: to.path})
}()
} else {
let is404 = to.matched.some(record => { // 404页面拦截
if(record.meta.role){
// 没有权限的页面,跳转的404页面
return record.meta.role.indexOf(store.getters.info.role) === -1
}
})
if(is404){
next({path: '/404'})
return false
}
next()
}
}
} else {
if (to.path === '/login') {
next()
}
next({path: '/login'})
}
})
actions: getInfo
// src/vuex/modules/role.js
state: {
info: '' // 每次刷新都要通过token请求个人信息来筛选动态路由
},
mutations: {
getInfo (state, info) {
// 省略 axios 请求代码 通过 token 向后台请求用户权限等信息,这里用假数据赋值
state.info = info
// 将 info 存储在 localStorage里, 按钮指令权限将会用到
localStorage.setItem("info", JSON.stringify(store.getters.info))
},
setRole (state, options) { // 切换角色,测试权限管理
state.info = {
role: options.role,
permissions: options.permissions
}
localStorage.setItem("info", JSON.stringify(store.getters.info))
// 权限切换后要根据新权限重新获取新路由,再走一遍流程
store.dispatch('newRoutes', options.role)
router.addRoutes(store.getters.addRouters)
}
},
actions: {
getInfo ({commit}, token) {
commit('getInfo', token)
},
setRole ({commit}, options){// 切换角色,测试权限管理,不需要可以删除
commit('setRole', options)
}
}
actions: newRoutes
// src/vuex/modules/routerData.js
import {defaultRouter, addRouter} from '@/router/index'
const routerData = {
state: {
routers: [],
addRouters: []
},
mutations: {
setRouters: (state, routers) => {
state.addRouters = routers // 保存动态路由用来addRouter
state.routers = defaultRouter.concat(routers) // 所有有权限的路由表,用来生成菜单列表
}
},
actions: {
newRoutes ({commit}, role) {
// 通过递归路由表,删除掉没有权限的路由
function eachSelect (routers, userRole) {
for (let j = 0; j < routers.length; j++) {
if (routers[j].meta && routers[j].meta.role.length && routers[j].meta.role.indexOf(userRole) === -1) {
// 如果没有权限就删除该路由,如果是父级路由没权限,所有子菜单就更没权限了,所以一并删除
routers.splice(j, 1)
j = j !== 0 ? j - 1 : j // 删除掉没有权限的路由后,下标应该停止 +1,保持不变,如果下标是 0的话删除之后依然等于0
}
if (routers[j].children && routers[j].children.length) {
// 如果包含子元素就递归执行
eachSelect(routers[j].children, userRole)
}
}
}
// 拷贝这个数组是因为做权限测试的时候可以从低级切回到高级角色,仅限演示,正式开发时省略这步直接使用 addRouter
// 仅限演示
let newArr = [...addRouter]
eachSelect(newArr, role)
commit('setRouters', newArr)
// 正式开发
// eachSelect(addRouter, role)
// commit('setRouters', addRouter)
}
}
}
export default routerData
按钮级别权限验证
通过自定义指令获取当前按钮所需的有哪些权限,然后和当前用户的权限对比,如果没有权限则删除按钮
// src/directive/permission/button.js
export default {
install (Vue, options) {
Vue.directive("roleBtn", {
inserted: function (el, binding) {
let roleArr = binding.value // 获取按钮所需权限
let userRole = JSON.parse(localStorage.getItem("info")).role// 获取当前用户权限
if (roleArr && roleArr.indexOf(userRole) !== -1) {
return false
} else {
el.parentNode.removeChild(el)
}
}
})
}
}
使用自定义指令权限
<el-button type="primary" plain size="medium">查看</el-button>
<el-button type="primary" plain size="medium" v-role-btn="['admin']">添加</el-button>
<el-button type="danger" plain size="medium" v-role-btn="['superAdmin']">删除</el-button>
<el-button type="primary" plain size="medium" v-role-btn="['superAdmin','admin']">修改</el-button>
系列文章
Vue2.0 + ElementUI 手写权限管理系统后台模板(一)——简述
Vue2.0 + ElementUI 手写权限管理系统后台模板(二)——权限管理