权限受控处理方案
- 权限理论:明确什么是
RBAC
权限控制体现 - 辅助业务:完善 用户、角色、权限 三个页面功能
- 核心功能:落地实现
RBAC
权限控制系统
# 权限理论:RBAC 权限控制体系
在我们当前的项目中,我们可以通过:
- 员工管理为用户指定角色
- 通过角色列表为角色指定权限
- 通过权限列表查看当前项目所有权限
以上三条就制定了一个用户由:用户 -> 角色 -> 权限 的一个分配关系。
当我们通过角色为某一个用户指定到不同的权限之后,那么该用户就会在 项目中体会到不同权限的功能
那么这样的一套关系就是我们的 RBAC 权限控制体系,也就是 基于 角色的权限 控制 用户的访问
# 辅助业务:角色列表展示
点击查看
辅助业务:
员工管理(用户列表)
- 为用户分配角色
角色列表
- 角色列表展示
- 为角色分配权限
权限列表
- 权限列表展示
创建
api/role
接口文件:import request from '@/utils/request' /** * 获取所有角色 */ export const roleList = () => { return request({ url: '/role/list' }) }
1
2
3
4
5
6
7
8
9
10
11在
views/role-list
中获取数据import { roleList } from '@/api/role' import { watchSwitchLang } from '@/utils/i18n' import { ref } from 'vue' const allRoles = ref([]) const getRoleList = async () => { allRoles.value = await roleList() } getRoleList() watchSwitchLang(getRoleList)
1
2
3
4
5
6
7
8
9
10通过 el-table (opens new window) 进行数据展示
<template> <div class=""> <el-card> <el-table :data="allRoles" border style="width: 100%"> <el-table-column :label="$t('msg.role.index')" type="index" width="120"> </el-table-column> <el-table-column :label="$t('msg.role.name')" prop="title"> </el-table-column> <el-table-column :label="$t('msg.role.desc')" prop="describe"> </el-table-column> <el-table-column :label="$t('msg.role.action')" prop="action" width="260" > <el-button type="primary" size="mini"> {{ $t('msg.role.assignPermissions') }} </el-button> </el-table-column> </el-table> </el-card> </div> </template>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 辅助业务:权限列表展示
点击查看
创建
api/permission
文件import request from '@/utils/request' /** * 获取所有权限 */ export const permissionList = () => { return request({ url: '/permission/list' }) }
1
2
3
4
5
6
7
8
9
10
11在
views/permission-list
获取数据<script setup> import { permissionList } from '@/api/permission' import { watchSwitchLang } from '@/utils/i18n' import { ref } from 'vue' /** * 权限分级: * 1. 一级权限为页面权限 * permissionMark 对应 路由名称 * 2. 二级权限为功能权限 * permissionMark 对应 功能权限表 */ // 所有权限 const allPermission = ref([]) const getPermissionList = async () => { allPermission.value = await permissionList() } getPermissionList() watchSwitchLang(getPermissionList) </script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19通过 el-table (opens new window) 进行数据展示
<template> <div class=""> <el-card> <el-table :data="allPermission" style="width: 100%; margin-bottom: 20px" row-key="id" border default-expand-all :tree-props="{ children: 'children', hasChildren: 'hasChildren' }" > <el-table-column prop="permissionName" :label="$t('msg.permission.name')" width="180" > </el-table-column> <el-table-column prop="permissionMark" :label="$t('msg.permission.mark')" width="180" > </el-table-column> <el-table-column prop="permissionDesc" :label="$t('msg.permission.desc')" > </el-table-column> </el-table> </el-card> </div> </template>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# 辅助业务:为用户分配角色
点击查看
创建为用户分配角色弹出层
views/user-manage/components/roles
<template> <el-dialog :title="$t('msg.excel.roleDialogTitle')" :model-value="modelValue" @close="closed" > 内容 <template #footer> <span class="dialog-footer"> <el-button @click="closed">{{ $t('msg.universal.cancel') }}</el-button> <el-button type="primary" @click="onConfirm">{{ $t('msg.universal.confirm') }}</el-button> </span> </template> </el-dialog> </template> <script setup> import { defineProps, defineEmits } from 'vue' defineProps({ modelValue: { type: Boolean, required: true } }) const emits = defineEmits(['update:modelValue']) /** 确定按钮点击事件 */ const onConfirm = async () => { closed() } /** * 关闭 */ const closed = () => { emits('update:modelValue', false) } </script> <style lang="scss" scoped></style>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46在
user-manage
中点击查看,展示弹出层<roles-dialog v-model="roleDialogVisible"></roles-dialog> import RolesDialog from './components/roles.vue' /** * 查看角色的点击事件 */ const roleDialogVisible = ref(false) const onShowRoleClick = row => { roleDialogVisible.value = true }
1
2
3
4
5
6
7
8
9
10
11在弹出层中我们需要利用 el-checkbox (opens new window) 进行数据展示,此时数据分为两种:
- 所有角色(已存在)
- 用户当前角色
所以我们需要先获取对应数据
在
api/user-manage
中定义获取用户当前角色接口/** * 获取指定用户角色 */ export const userRoles = (id) => { return request({ url: `/user-manage/role/${id}` }) }
1
2
3
4
5
6
7
8在
views/user-manage/components/roles
组件中获取所有角色数据import { defineProps, defineEmits, ref } from 'vue' import { roleList } from '@/api/role' import { watchSwitchLang } from '@/utils/i18n' ... // 所有角色 const allRoleList = ref([]) // 获取所有角色数据的方法 const getListData = async () => { allRoleList.value = await roleList() } getListData() watchSwitchLang(getListData) // 当前用户角色 const userRoleTitleList = ref([])
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16在
views/user-manage/components/roles
组件,利用 el-checkbox (opens new window) 渲染所有角色<el-checkbox-group v-model="userRoleTitleList"> <el-checkbox v-for="item in allRoleList" :key="item.id" :label="item.title" ></el-checkbox> </el-checkbox-group>
1
2
3
4
5
6
7接下来渲染选中项,即:用户当前角色
调用
userRoles
接口需要 当前用户 ID,所以我们需要定义对应的props
const props = defineProps({ ... userId: { type: String, required: true } })
1
2
3
4
5
6
7接下来我们可以根据
userId
获取数据,但是这里大家要注意:因为该userId
需要在user-manage
用户点击之后获取当前点击行的id
。所以在roles
组件的初始状态下,获取到的userId
为null
。 因此我们想要根据userId
获取用户当前角色数据,我们需要watch userId
在userId
有值的前提下,获取数据,在roles
中// 当前用户角色 const userRoleTitleList = ref([]) // 获取当前用户角色 const getUserRoles = async () => { const res = await userRoles(props.userId) userRoleTitleList.value = res.role.map(item => item.title) } watch( () => props.userId, val => { if (val) getUserRoles() } )
1
2
3
4
5
6
7
8
9
10
11
12
13在
user-manage
中传递数据<roles-dialog v-model="roleDialogVisible" :userId="selectUserId" ></roles-dialog> const selectUserId = ref('') const onShowRoleClick = row => { selectUserId.value = row._id }
1
2
3
4
5
6
7
8
9在
dialog
关闭时重置selectUserId
(在点击同一个角色的时候,不会重新触发接口)// 保证每次打开重新获取用户角色数据 watch(roleDialogVisible, val => { if (!val) selectUserId.value = '' })
1
2
3
4在
api/user-manage
中定义分配角色接口/** * 分用户分配角色 */ export const updateRole = (id, roles) => { return request({ url: `/user-manage/update-role/${id}`, method: 'POST', data: { roles } }) }
1
2
3
4
5
6
7
8
9
10
11
12点击确定调用接口
/** 确定按钮点击事件 */ const i18n = useI18n() const onConfirm = async () => { // 处理数据结构 const roles = userRoleTitleList.value.map(title => { return allRoleList.value.find(role => role.title === title) }) await updateRole(props.userId, roles) ElMessage.success(i18n.t('msg.role.updateRoleSuccess')) closed() }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15修改成功后,发送事件
const emits = defineEmits(['update:modelValue', 'updateRole']) const onConfirm = async () => { ... // 角色更新成功 emits('updateRole') }
1
2
3
4
5
6
7在
user-manage
中监听角色更新成功事件,重新获取数据<roles-dialog v-model="roleDialogVisible" :userId="selectUserId" @updateRole="getListData" ></roles-dialog>
1
2
3
4
5
# 辅助业务:为角色指定权限
为角色指定权限通过 弹出层中的 树形控件 (opens new window) 处理,整体的流程与上一小节相差无几。
点击查看
创建
views/role-list/components/DistributePermission.vue
为角色指定权限弹出层<template> <el-dialog :title="$t('msg.excel.roleDialogTitle')" :model-value="modelValue" @close="closed" > 内容 <template #footer> <span class="dialog-footer"> <el-button @click="closed">{{ $t('msg.universal.cancel') }}</el-button> <el-button type="primary" @click="onConfirm">{{ $t('msg.universal.confirm') }}</el-button> </span> </template> </el-dialog> </template> <script setup> import { defineProps, defineEmits } from 'vue' defineProps({ modelValue: { type: Boolean, required: true } }) const emits = defineEmits(['update:modelValue']) /** 确定按钮点击事件 */ const onConfirm = async () => { closed() } /** * 关闭 */ const closed = () => { emits('update:modelValue', false) } </script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43在
roles-list
中点击查看,展示弹出层<template> <div class=""> <el-card> <el-table :data="allRoles" border style="width: 100%"> ... <el-table-column ... #default="{ row }" > <el-button type="primary" size="mini" @click="onDistributePermissionClick(row)" > {{ $t('msg.role.assignPermissions') }} </el-button> </el-table-column> </el-table> </el-card> <distribute-permission v-model="distributePermissionVisible" ></distribute-permission> </div> </template> <script setup> ... import DistributePermission from './components/DistributePermission.vue' ... /** * 分配权限 */ const distributePermissionVisible = ref(false) const onDistributePermissionClick = row => { distributePermissionVisible.value = true } </script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41在弹出层中我们需要利用 el-tree (opens new window) 进行数据展示,此时数据分为两种:
- 所有权限(已存在)
- 角色对应的权限
所以我们需要先获取对应数据
在
api/role
中定义获取角色当前权限/** * 获取指定角色的权限 */ export const rolePermission = (roleId) => { return request({ url: `/role/permission/${roleId}` }) }
1
2
3
4
5
6
7
8在
DistributePermission
组件中获取所有权限数据<script setup> import { defineProps, defineEmits, ref } from 'vue' import { permissionList } from '@/api/permission' import { watchSwitchLang } from '@/utils/i18n' ... // 所有权限 const allPermission = ref([]) const getPermissionList = async () => { allPermission.value = await permissionList() } getPermissionList() watchSwitchLang(getPermissionList) ... </script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16使用 el-tree (opens new window) 渲染权限数据
<template> ... <el-tree ref="treeRef" :data="allPermission" show-checkbox check-strictly node-key="id" default-expand-all :props="defaultProps" > </el-tree> ... </template> <script setup> ... // 属性结构配置 const defaultProps = { children: 'children', label: 'permissionName' } ... </script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25接下来渲染选中项,即:角色当前权限
调用
rolePermission
接口需要 当前角色 ID,所以我们需要定义对应的props
const props = defineProps({ modelValue: { type: Boolean, required: true }, roleId: { type: String, required: true } })
1
2
3
4
5
6
7
8
9
10在
role-list
中传递角色ID<distribute-permission v-model="distributePermissionVisible" :roleId="selectRoleId" ></distribute-permission> /** * 分配权限 */ const selectRoleId = ref('') const onDistributePermissionClick = row => { selectRoleId.value = row.id }
1
2
3
4
5
6
7
8
9
10
11
12
13调用
rolePermission
接口获取数据import { rolePermission } from '@/api/role' // 获取当前用户角色的权限 const getRolePermission = async () => { const checkedKeys = await rolePermission(props.roleId) console.log(checkedKeys) } watch( () => props.roleId, val => { if (val) getRolePermission() } )
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16根据获取到的数据渲染选中的
tree
// tree 节点 const treeRef = ref(null) // 获取当前用户角色的权限 const getRolePermission = async () => { const checkedKeys = await rolePermission(props.roleId) treeRef.value.setCheckedKeys(checkedKeys) }
1
2
3
4
5
6
7
8在
api/role
中定义分配权限接口/** * 为角色修改权限 */ export const distributePermission = (data) => { return request({ url: '/role/distribute-permission', method: 'POST', data }) }
1
2
3
4
5
6
7
8
9
10
11点击确定调用接口
import { rolePermission, distributePermission } from '@/api/role' import { useI18n } from 'vue-i18n' import { ElMessage } from 'element-plus' /** 确定按钮点击事件 */ const i18n = useI18n() const onConfirm = async () => { await distributePermission({ roleId: props.roleId, permissions: treeRef.value.getCheckedKeys() }) ElMessage.success(i18n.t('msg.role.updateRoleSuccess')) closed() }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 基于 RBAC 的权限控制体系原理与实现分析
核心内容:基于 RBAC 的权限控制 ,在之前我们的 权限理论 这一小节的时候说过 RBAC
是基于 用户 -> 角色 -> 权限 的 基于 角色的权限 控制 用户的访问 的体系。
在这套体系中,最基层的就是 权限部分 。那么这个权限部分在我们的项目中具体的呈现是什么呢?那么下面我们就来看一下:
点击查看
- 我们可以先为 员工角色 指定 空权限
- 然后为我们的 测试用户 指定指定 员工角色
- 此时我们重新登录 测试用户
- 可以发现左侧菜单中仅存在 个人中心 页面
- 然后我们重新登录 超级管理员 账号
- 为 员工角色 指定 员工管理 && 分配角色 权限
- 然后为我们的 测试用户 指定指定 员工角色
- 此时我们重新登录 测试用户
- 可以发现左侧菜单中多出 员工管理 页面,并且页面中仅存在指定的 分配角色 功能
# 权限系统分成两部分:
- 页面权限:比如 员工管理;页面权限 表示:当前用户可以访问的页面
- 功能权限:比如 分配角色;功能权限 表示:当前用户可以访问的权限功能(PS:并非所有功能有需要权限)
# 页面权限:
- 用户可看到的:左侧
menu
菜单的item
展示 - 用户看不到的:路由表配置
左侧 menu
菜单是根据路由表自动生成的。 所以以上第一部分的内容其实就是由第二部分引起的。
路由表配置
- 私有路由表
privateRoutes
:依据权限进行动态配置的 - 公开路由表
publicRoutes
:无权限要求的
实现页面权限核心的点就是在我们的 私有路由表 privateRoutes
。根据不同的权限数据,生成不同的私有路由表
对于
vue-router 4
而言,提供了 addRoute API (opens new window) ,可以 动态添加路由到路由表中,那么我们就可以利用这个API
生成不同的路由表数据。
# 功能权限
根据不同的 权限数据,展示不同的 功能按钮
我们只需要:根据权限数据,隐藏功能按钮 即可
# 页面权限实现步骤:
- 获取 权限数据
- 私有路由表 不再被直接加入到
routes
中 - 利用
addRoute API
动态添加路由到 路由表 中
# 功能权限实现步骤:
- 获取 权限数据
- 定义 隐藏按钮方式(通过指令)
- 依据数据隐藏按钮
# 业务落地:定义页面权限控制动作,实现页面权限受控
页面权限数据在
userInfo -> permission -> menus
之中私有路由表 不再被直接加入到
routes
中export const privateRoutes = [...] export const publicRoutes = [...] const router = createRouter({ history: createWebHashHistory(), routes: publicRoutes })
1
2
3
4
5
6
7
实现第三步:利用 addRoute API (opens new window) 动态添加路由到 路由表 中
定义添加的动作,该动作我们通过一个新的
vuex
模块进行创建
store/modules/permission
模块// 专门处理权限路由的模块 import { publicRoutes, privateRoutes } from '@/router' export default { namespaced: true, state: { // 路由表:初始拥有静态路由权限 routes: publicRoutes }, mutations: { /** * 增加路由 */ setRoutes(state, newRoutes) { // 永远在静态路由的基础上增加新路由 state.routes = [...publicRoutes, ...newRoutes] } }, actions: { /** * 根据权限筛选路由 * menus 权限数据 */ filterRoutes(context, menus) { } } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27那么
filterRoutes
这个动作我们怎么制作呢?我们可以为每个权限路由指定一个
name
,每个name
对应一个 页面权限通过
name
与 页面权限 匹配的方式筛选出对应的权限路由创建
router/modules
文件夹UserManage.js
import layout from '@/layout' export default { path: '/user', component: layout, redirect: '/user/manage', name: 'userManage', meta: { title: 'user', icon: 'personnel' }, children: [ { path: '/user/manage', component: () => import('@/views/user-manage/index'), meta: { title: 'userManage', icon: 'personnel-manage' } }, { path: '/user/info/:id', name: 'userInfo', component: () => import('@/views/user-info/index'), props: true, meta: { title: 'userInfo' } }, { path: '/user/import', name: 'import', component: () => import('@/views/import/index'), meta: { title: 'excelImport' } } ] }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40RoleList.js
import layout from '@/layout' export default { path: '/user', component: layout, redirect: '/user/manage', name: 'roleList', meta: { title: 'user', icon: 'personnel' }, children: [ { path: '/user/role', component: () => import('@/views/role-list/index'), meta: { title: 'roleList', icon: 'role' } } ] }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23PermissionList.js
import layout from '@/layout' export default { path: '/user', component: layout, redirect: '/user/manage', name: 'roleList', meta: { title: 'user', icon: 'personnel' }, children: [ { path: '/user/permission', component: () => import('@/views/permission-list/index'), meta: { title: 'permissionList', icon: 'permission' } } ] }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23Article.js
import layout from '@/layout' export default { path: '/article', component: layout, redirect: '/article/ranking', name: 'articleRanking', meta: { title: 'article', icon: 'article' }, children: [ { path: '/article/ranking', component: () => import('@/views/article-ranking/index'), meta: { title: 'articleRanking', icon: 'article-ranking' } }, { path: '/article/:id', component: () => import('@/views/article-detail/index'), meta: { title: 'articleDetail' } } ] }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27ArticleCreate.js
import layout from '@/layout' export default { path: '/article', component: layout, redirect: '/article/ranking', name: 'articleCreate', meta: { title: 'article', icon: 'article' }, children: [ { path: '/article/create', component: () => import('@/views/article-create/index'), meta: { title: 'articleCreate', icon: 'article-create' } }, { path: '/article/editor/:id', component: () => import('@/views/article-create/index'), meta: { title: 'articleEditor' } } ] }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27在
router/index
中合并这些路由到privateRoutes
中import ArticleCreaterRouter from './modules/ArticleCreate' import ArticleRouter from './modules/Article' import PermissionListRouter from './modules/PermissionList' import RoleListRouter from './modules/RoleList' import UserManageRouter from './modules/UserManage' export const asyncRoutes = [ RoleListRouter, UserManageRouter, PermissionListRouter, ArticleCreaterRouter, ArticleRouter ]
1
2
3
4
5
6
7
8
9
10
11
12
13- 此时所有的 权限页面 都拥有一个名字,这个名字与 权限数据 匹配,所以我们就可以据此生成 权限路由表数据
/** * 根据权限筛选路由 */ filterRoutes(context, menus) { const routes = [] // 路由权限匹配 menus.forEach(key => { // 权限名 与 路由的 name 匹配 routes.push(...privateRoutes.filter(item => item.name === key)) }) // 最后添加 不匹配路由进入 404 routes.push({ path: '/:catchAll(.*)', redirect: '/404' }) context.commit('setRoutes', routes) return routes }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18- 在
store/index
中设置该modules
... export default createStore({ getters, modules: { ... permission } })
1
2
3
4
5
6
7
8- 在
src/permission
中,获取用户数据之后调用该动作
// 判断用户资料是否获取 // 若不存在用户信息,则需要获取用户信息 if (!store.getters.hasUserInfo) { // 触发获取用户信息的 action,并获取用户当前权限 const { permission } = await store.dispatch('user/getUserInfo') // 处理用户权限,筛选出需要添加的权限 const filterRoutes = await store.dispatch( 'permission/filterRoutes', permission.menus ) // 利用 addRoute 循环添加 filterRoutes.forEach(item => { router.addRoute(item) }) // 添加完动态路由之后,需要在进行一次主动跳转(进行主动跳转,添加的路由才能生效) return next(to.path) } next()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18因为我们主动获取了
getUserInfo
动作的返回值,所以不要忘记在getUserInfo
中return res
那么到这里,当我们更换用户之后,刷新页面,路由表即可动态生成。
# 业务落地:重置路由表数据
遇到问题:重新登录权限账户,不刷新页面,左侧菜单不会自动改变。
问题原因:退出登录时,添加的路由表并未被删除
解决方案:只需要在退出登录时,删除动态添加的路由表即可。删除动态添加的路由可以使用 removeRoute (opens new window) 方法进行。
在
router/index
中定义resetRouter
方法/** * 初始化路由表 */ export function resetRouter () { if ( store.getters.userInfo && store.getters.userInfo.permission && store.getters.userInfo.permission.menus ) { const menus = store.getters.userInfo.permission.menus menus.forEach((menu) => { router.removeRoute(menu) }) } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15在退出登录的动作下,触发该方法
import router, { resetRouter } from '@/router' logout(context) { resetRouter() ... }
1
2
3
4
5
6
# 业务落地:创建功能受控指令
在前面分析 功能权限 时,我们说过,实现功能权限的核心在于 根据数据隐藏功能按钮,那么隐藏的方式我们可以通过指令进行。
首先创建这样一个指令(vue3 自定义指令 (opens new window))
我们期望最终可以通过这样格式的指令进行功能受控
v-permission="['importUser']"
以此创建对应的自定义指令
directives/permission
import store from '@/store' function checkPermission(el, binding) { // 获取绑定的值,此处为权限 const { value } = binding // 获取所有的功能指令 const points = store.getters.userInfo.permission.points // 当传入的指令集为数组时 if (value && value instanceof Array) { // 匹配对应的指令 const hasPermission = points.some(point => { return value.includes(point) }) // 如果无法匹配,则表示当前用户无该指令,那么删除对应的功能按钮 if (!hasPermission) { el.parentNode && el.parentNode.removeChild(el) } } else { // eslint-disabled-next-line throw new Error('v-permission value is ["admin","editor"]') } } export default { // 在绑定元素的父组件被挂载后调用 mounted(el, binding) { checkPermission(el, binding) }, // 在包含组件的 VNode 及其子组件的 VNode 更新后调用 update(el, binding) { checkPermission(el, binding) } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34在
directives/index
中绑定该指令... import permission from './permission' export default (app) => { ... app.directive('permission', permission) }
1
2
3
4
5
6
7
8在所有功能中,添加该指令
views/role-list/index
<el-button ... v-permission="['distributePermission']" > {{ $t('msg.role.assignPermissions') }} </el-button>
1
2
3
4
5
6views/user-manage/index
<el-button ... v-permission="['importUser']" > {{ $t('msg.excel.importExcel') }}</el-button >
1
2
3
4
5
6<el-button ... v-permission="['distributeRole']" >{{ $t('msg.excel.showRole') }}</el-button >
1
2
3
4
5<el-button ... v-permission="['removeUser']" >{{ $t('msg.excel.remove') }}</el-button >
1
2
3
4
5
# 总结
核心就是 RBAC
的权限受控体系 。围绕着 用户->角色->权限 的体系是现在在包含权限控制的系统中使用率最广的一种方式。