后台开发离不开权限,不一样的用户登陆,根据不一样的权限,能够访问不一样的管理目录。但在使用 vue-element-template 里内置的权限模块功能后,发现做者提供的权限模块扩展性不是很好,因此这篇文章就是记录我是如何基于做者原有的权限模块进行的一次重构。javascript
为何要重构
开篇的时候说了,由于扩展性不是很好,那具体是什么扩展性很差呢?咱们先来看下做者原有的权限模块实现思路是怎么样的。html
首先须要在路由里配置 roles 角色字段,表明该角色能够访问这个路由,能够配置多个角色,例如:vue
{
path: 'page',
component: () => import('@/views/permission/page'),
name: 'PagePermission',
meta: {
title: 'Page Permission',
roles: ['admin', 'editor']
}
}
而后在登陆成功时,根据用户角色过滤生成能访问的路由,最后经过 addRoutes 进行动态路由挂载,这部分的实现可参考 permission.js 文件。java
这样的实现有个缺点,就是角色没法动态增长,好比要增长一个角色,就须要到路由里将这个角色须要的权限所有添加一遍,删除修改同理,而这部分的操做没法交给用户或者管理员自行配置。git
开始重构
依托于这样一个痛点,重构的思路其实就出来了,将路由里配置的角色改为具体的权限便可。github
路由配置
我先把权限划分出了四大类:数组
browse 浏览权限
create 新增权限
edit 编辑权限
delete 删除权限
再结合不一样模块,就能够组成这样一个字符串 [moduleName].[authType] ,例如新闻管理模块下的新增权限就是 news.create,最后将路由文件里 roles 字段替换成对应的模块权限便可,固然如今不能叫 roles ,我将字段名也替换成了 auth ,就像这样:框架
{
path: 'page',
component: () => import('@/views/permission/page'),
name: 'PagePermission',
meta: {
title: 'Page Permission',
auth: ['module.browse', 'module.create']
}
}
接口配合
接口要如何配合咱们修改呢?原先接口只须要返回当前用户的角色便可,如今则须要返回具体的权限列表,例如这样一个数组:async
[
'news.browse',
'news.create',
'news.edit',
'category.browse',
'category.create',
'category.edit',
'category.delete',
'log.browse',
...
]
生成路由
这部分就是要修改原有根据用户角色生成能访问的路由的代码,这部分代码逻辑在全局状态 permission 里,能够看到 actions里有个叫 generateRoutes 的方法,就是用于根据用户角色生成路由并返回的。post
这部分改动比较大,具体直接看代码吧:
import { asyncRoutes, constantRoutes } from '@/router'
function hasAuthorization(authorization, route) {
if (route.meta && route.meta.auth) {
return authorization.some(auth => {
return route.meta.auth.some(routeAuth => {
return routeAuth === auth
})
})
} else {
return true
}
}
export function filterAsyncRoutes(routes, authorization) {
const res = []
routes.forEach(route => {
const tmp = { ...route }
if (hasAuthorization(authorization, tmp)) {
if (tmp.children) {
tmp.children = filterAsyncRoutes(tmp.children, authorization)
}
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 }, authList) {
return new Promise(resolve => {
const accessedRoutes = filterAsyncRoutes(asyncRoutes, authList)
commit('SET_ROUTES', accessedRoutes)
resolve(accessedRoutes)
})
}
}
export default {
namespaced: true,
state,
mutations,
actions
}
固然了,在 permission.js 里调用 generateRoutes 这个方法的时候,入参也要修改掉,须要把用户权限列表传入进来。
这样,最终生成好的路由就是根据用户权限过滤出来的可访问路由了。
按钮级别权限控制
原框架中并无提供按钮级别的权限控制,但业务中确定避免不了这样的需求,例以下面这个员工管理的列表页。
这样一个页面,就恰好涵盖了权限四大类型,首先列表页是否能访问,是 browse 权限,页面中“添加新员工”按钮是 create权限,列表中“编辑”和“删除”按钮分别是 edit 和 delete 权限,但这时候若是用户只有 browse 和 edit 权限的话,那对应没有权限的按钮,就应该在页面上去掉。
其实实现这个很简单,只须要本身写一个鉴权的方法就能够了,个人作法是在全局状态 permission 里增长一个叫 hasAuthorization 的 getters :
const getters = {
hasAuthorization: state => authorization => {
return state.authorization.some(auth => {
return auth === authorization
})
}
}
而后在须要作权限控制的按钮上就能够调用了。
添加新员工
自定义指令
$store.getters['permission/hasAuthorization']('xxx.yyy') 这样的方式仍是过于麻烦了,毕竟要写这么一长端代码,因而我想到了 Vue 的自定义指令。
我把鉴权部分封装成一个方法,同时注册了 v-auth 的全局指令,而且还把鉴权的方法挂载到 Vue 的原型链上,方便功能点的鉴权。
const auth = value => {
let auth
if (typeof value === 'string') {
auth = store.getters['permission/hasAuthorization'](value)
} else {
auth = value.some(item => {
return store.getters['permission/hasAuthorization'](item)
})
}
return auth
}
// 注册 v-auth 指令
Vue.directive('auth', {
inserted: (el, binding) => {
if (!auth(binding.value)) {
el.remove()
}
}
})
// 挂载 this.$auth() 方法
Vue.prototype.$auth = auth
这个 v-auth 指令支持传入数组格式,只要数组其中一项知足则鉴权成功,若是须要数组每一项都知足才算成功,能够自行再注册一个全局指令实现。
完成重构
这样就完成了对权限模块的重构,由于路由直接和权限对接,用户就能够自行去配置角色的权限了,例如这样:
参考
2019/07/31 更新:
昨天看到了《iView 2019 新品发布会录像》的视频,发现 iView Admin Pro 里提供的鉴权,和我这篇文章里提供的部分鉴权的思路,能够说是一模一样,能够说是英雄所见略同。
尤为是自定义指令这部分,都采用了 v-auth 的指令,也一样提供了单个和多个权限的鉴权,固然也有部分各自的特色,好比我在实际项目中还提供了 v-auth-all 指令,iView 则还提供了一个 的组件作功能块的鉴权。