乐趣区

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’ // 路由名字 name
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

// 不需要权限的路由
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: ‘*’,
redirect: ‘/404’,
hidden: true,
children: []
},

]
export default new Router({
routes: defaultRouter
})
export {defaultRouter, addRouter}

然后通过 token 获取当前登录用户的个人信息,在 router 被挂载到 Vue 之前和需要权限的路由表做对比,筛选出当前角色的动态路由表,
// main.js
// 获取角色信息,根据用户权限动态加载路由
router.beforeEach((to, from, next) => {
if (store.getters.token) {// 查看 token 是否存在
store.dispatch(‘setToken’, store.getters.token) // 每次操作都重新写入 token,延长有效会话时间
if (to.path === ‘/login’) {
next({path: ‘/’})
} else {
if (!store.getters.info.role) {// 查看是否有当前用户角色,如果没有则获取角色信息
!async function getAddRouters () {
await store.dispatch(‘getInfo’, store.getters.token) // 通过 token 获取角色信息
await store.dispatch(‘newRoutes’, store.getters.info.role) // 通过权限筛选新路由表
await router.addRoutes(store.getters.addRouters) // 动态加载新路由表
next({path: ‘/index’})
}()
} 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, token) {
// 省略 axios 请求代码 通过 token 向后台请求用户权限等信息,这里用假数据赋值
state.info = {
role: ‘superAdmin’,
permissions: ‘ 超级管理员 ’
}
// 将 info 存储在 sessionStorage 里,按钮指令权限将会用到
sessionStorage.setItem(‘info’, JSON.stringify(store.getters.info))
},
setRole (state, options) {// 切换角色,测试权限管理
state.info = {
role: options.role,
permissions: options.permissions
}
sessionStorage.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
按钮级别权限验证
通过自定义指令获取当前按钮所需的有哪些权限,然后和当前用户的权限对比,如果没有权限则删除按钮
// btnPermission.js
import Vue from ‘vue’

Vue.directive(‘roleBtn’,{
bind:function (el,binding) {
let roleArr = binding.value; // 获取按钮所需权限
let userRole = JSON.parse(sessionStorage.getItem(‘info’)).role // 获取当前用户权限
if (roleArr && roleArr.indexOf(userRole) !== -1) {
return false
} else {
el.parentNode.removeChild(el);
}
}
})
export default Vue
使用自定义指令权限
<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>

退出移动版