RBAC权限管理(详细)

2022-08-04 08:28:26

RBAC权限设计思想

为了达成不同账号(员工、总裁)登录系统后看到不同页面,执行不同功能,RBAC(Role-Based Access control)权限模型,就是根据角色的权限,分配可视页面。

三个关键点:

用户:使用系统的人
角色:使用系统的人是什么职位(员工、经理、总裁)
权限点:职位可以做的事情(左侧菜单栏中的功能模块——>增删改查)
在这里插入图片描述
测试流程
①在员工管理页新增员工这是三要素中的用户
②为新增的员工分配角色
③在公司设置里为角色分配权限

💢系统中的权限不能随意添加,必须是以开发出来的权限(左侧菜单栏里可实现的页面)
💢用户和角色之间是一对多的关系,一个人身兼数职。

具体实现

1.实现分配角色
点击分配角色、弹出框,框里含有已有角色列表,点击分配角色时将id传过去,根据id显示当前用户已有的角色。

分配角色父组件src/employees/employee.vue

<template slot-scope="scope"><el-button type="text" size="smell" @click="assignFn(scope.row)">分配角色</el-button></template><el-dialog
      title="分配角色":visible.sync="showDialogRole":close-on-press-escape="false":close-on-click-modal="false"
      @close="showDialogRole=false"><assign-role:id="curId" ref="assignRole" @close="showDialogRole=false"/></el-dialog>//import 导入复原框子组件// -------------------------------------------分配角色----------------------------------assignFn(row){this.showDialogRole=truethis.curId= row.idthis.$nextTick(()=>{this.$refs.assignRole.getRoleListFn()})}

分配角色的子组件:employees/assignRole.vue

<template><div><el-checkbox-group v-model="rolesList"><el-checkbox v-for="item in checkList":key="item.id":label="item.id">{{ item.name}}</el-checkbox></el-checkbox-group><div style="margin-top: 20px; text-align: right"><el-button type="primary" @click="submitFn">确定</el-button><el-button @click="closeDialog">取消</el-button></div></div></template><script>import{ getAllRoleAPI}from'@/api/settings'import{ getDetailInfo}from'@/api/user.js'import{ assignRolesAPI}from'@/api/employees.js'exportdefault{
  name:'AssignRole',
  props:{
    id:{ type: String,	required:true}},data(){return{
      checkList:[],// 角色列表
      rolesList:[]// 用户已有角色}},created(){},
  methods:{// ------------------------------------提交角色-----------------------------------asyncsubmitFn(){const resp=awaitassignRolesAPI({ id:this.id, roleIds:this.rolesList})
      console.log(resp)this.$emit('close')},// ----------------------------------获取角色列表----------------------------------asyncgetRoleListFn(){const resp=awaitgetAllRoleAPI({ page:1, pagesize:100})
      console.log(resp)this.checkList= resp.data.rowsconst res=awaitgetDetailInfo(this.id)
      console.log(res)this.rolesList= res.data.roleIds},// -------------------------------------取消按钮------------------------------------closeDialog(){this.$emit('close')}}}</script>

💢《el-checkbox-group v-model=“rolesList”》中v-model绑定的值是数组表示可多选。
💢在模板中渲染数据时

{{ item.name }}
其中label决定当前选中的值,{{要展示的角色名称}}

2.实现分配权限

父组件中(views/setings/setings.vue):准备弹框 -> 注册事件 -> 提供数据方法

<template><divclass="settings-container"><divclass="app-container"><el-card><!-- 具体页面结构--><el-tabs><!-- 放置页签--><el-tab-pane label="角色管理"><!-- 表格--><el-table:data="tableList"><el-table-column label="操作"><!-- scope只是插槽占位置的名字而已,重要的是里面的.row这是每一行的对象,是固定写法--><template slot-scope="scope"><el-button size="small" type="success" @click="hAssign(scope.row.id)">分配权限</el-button></template></el-table-column></el-table><el-row type="flex" justify="center" align="middle" style="height: 60px"></el-row></el-tab-pane></el-tabs></el-card><!-- 分配权限的弹层--><el-dialog
        title="分配权限(一级为路由页面查看权限-二级为按钮操作权限)":visible.sync="showDialogAssign"><assign-permission ref="assignPermission":role-id="roleId" @close="showDialogAssign=false"/></el-dialog></div></div></template><script>exportdefault{
  name:'Setting',
  components:{
    assignPermission},data(){return{
      showDialogAssign:false,// 分配权限对话框
  methods:{// -----------------------------------------分配权限-------------------------------------hAssign(id){this.roleId= idthis.showDialogAssign=truethis.$nextTick(()=>{this.$refs.assignPermission.getRoleDetail()})}}}</script>

子组件中(settings/assignPermission.vue):

<template><div><!-- 权限点数据展示:check-strictly  设置true,可以关闭父子关联--><el-tree
      ref="tree":data="permissionData":props="{ label: 'name' }"
      node-key="id"default-expand-all:show-checkbox="true":check-strictly="true"/><div style="text-align:right;"><el-button @click="hCancel">取消</el-button><el-button type="primary" @click="getAssignRoleFn">确定</el-button></div></div></template><script>import{ getPermissionListAPI}from'@/api/permissions.js'import{ tranListToTreeData}from'@/utils/index.js'import{ getRoleDetail, getAssignRoleAPI}from'@/api/settings.js'exportdefault{
  props:{
    roleId:{
      type: String,
      required:true}},data(){return{
      permissionData:[]// 存储权限数据}},created(){this.getPermissionListFn()},
  methods:{// -------------------------------------------获取权限列表-------------------------------------asyncgetPermissionListFn(){const resp=awaitgetPermissionListAPI()
      console.log(resp)this.permissionData=tranListToTreeData(resp.data)
      console.log('数组转树',this.permissionData)},// --------------------------------------------获取角色详情------------------------------------asyncgetRoleDetail(){const resp=awaitgetRoleDetail(this.roleId)
      console.log(resp)// 回填到树上this.$refs.tree.setCheckedKeys(resp.data.permIds)},// ---------------------------------------------关闭弹层------------------------------------------hCancel(){// 通过父组件去关闭弹层this.$emit('close')// 下次根据id获取角色权限数组时,将容器清空,以免影响下次保存this.$refs.tree.setCheckedKeys([])},// ------------------------------------------给角色分配权限--------------------------------------asyncgetAssignRoleFn(){const pid=this.$refs.tree.getCheckedKeys()const resp=awaitgetAssignRoleAPI({ id:this.roleId, permIds: pid})
      console.log(resp)this.hCancel()// 通知父组件关闭弹层this.$message.success('分配成功')}}}</script><style></style>

3.页面权限控制

1. 左侧菜单权限控制(不同的用户进来系统之后,看到的菜单是不同的)
 2. 操作按钮权限控制 (页面上的按钮,不同的人也有不同权限)
 3. 权限数据所在位置:下图是管理员登录时,可以看到的权限。![在这里插入图片描述](https://img-blog.csdnimg.cn/5bb6a3a086d6498c911224f19bc2840e.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBA5YuH5pWi54mb54mb77yM5Yay5Yay5Yay,size_11,color_FFFFFF,t_70,g_se,x_16)3.1修改权限数据

只有管理员才可修改权限数据,所以要先新增用户——>分配角色——>分配权限、重新等新用户账号观察权限数据(data.roles.menus,
points)

3.2 动态生成左侧菜单
新用户登录成功页面跳转、进入导航守卫
在这里插入图片描述
3.3 在router/index.js中的路由配置中删除动态路由的部分改为:routes: […constantRoutes]
3.4 在permission.js中引入动态路由,并使用addRoutes动态添加,此时左侧动态路由只剩下静态首页了,可在地址栏输入地址实现跳转(addRoutes的作用)
3.5 从actions中返回菜单项

asyncgetUserInfo(context){// 1. ajax获取基本信息,包含用户idconst rs=awaitgetUserInfoApi()
      console.log('用来获取用户信息的,', rs)// 2. 根据用户id(rs.data.userId)再发请求,获取详情(包含头像)const info=awaitgetUserDetailById(rs.data.userId)
      console.log('获取详情', info.data)// 把上边获取的两份合并在一起,保存到vuex中
      context.commit('setUserInfo',{...info.data,...rs.data})// 当前用户可以看到的菜单 res.data.roles.menus+return rs.data.roles.menus},

3.6 在permission.js中获取action的返回值并过滤

/引入所有的动态路由表(未经过筛选)+import router,{ asyncRoutes}from'@/router'const whiteList=['/login','/404']
router.beforeEach(async(to,from, next)=>{// 开启进度条
  NProgress.start()// 获取本地token 全局getterconst token= store.getters.tokenif(token){// 有tokenif(to.path==='/login'){next('/')}else{if(!store.getters.userId){+const menus=await store.dispatch('user/getUserInfo')//根据权限过滤动态数组+const filterRoutes= asyncRoutes.filter(route=>{+const routeName= route.children[0].name+return menus.includes(routeName)+})// 1.改写成动态添加的方式+       router.addRoutes(filterRoutes)//2. 生成左侧菜单时,也应该去vuex中拿+       store.commit('menu/setMenuList', filterRoutes)+//3.解决刷新时出现的白屏bugnext({...to,// 保证路由添加完了再进入页面(可理解为重新进一次)
          replace:true// 重新进一次,不保留重复历史})}else{next()}}else{// 没有tokenif(whiteList.includes(to.path)){next()}else{next('/login')}}// 结束进度条
  NProgress.done()})
 💢当前的菜单(src\layout\components\Sidebar\index.vue)使用的数据:this.$router.options.routes **可以拿到当前路由配置,设置的路由表数据**但是这个数据是固定的,所以将此数据换为 this.$router.options.routes就可以动态拿到路由表的数据。

💢如果想调用addRoutes方法之后,路由表数据立刻在左侧菜单栏中显示,那就将动态路由菜单保存在vuex中

3.7修复bug
3.7.1解决刷新出现的白屏(路由守卫中的 //3…)
3.7.2退出后,再次登陆,发现菜单异常 (控制台有输出说路由重复)
在这里插入图片描述
原因:路由设置是通过router.addRoutes(filterRoutes)来添加的,退出时,并没有清空,再次登陆,又加了一次,所以有重复。
需要将路由权限重置 (恢复默认) 将来登录后再次追加才可以,不然的话,就会重复添加
解决:
router/index.js文件,有一个重置路由方法

// 重置路由exportfunctionresetRouter(){const newRouter=createRouter()
  router.matcher= newRouter.matcher// 重新设置路由的可匹配路径}

在登出的时候, 调用一下即可store/modules/user.js

import{ resetRouter}from'@/router'// 退出的action操作logout(context){// 1. 移除vuex个人信息
  context.commit('removeUserInfo')// 2. 移除token信息
  context.commit('removeToken')// 3. 重置路由+resetRouter()}

4.按钮级控制

4.1 自定义指令:自己定义的指令,因为本身指令不够用,所以我们需要自已去定义。

4.2 解决按钮级别的权限验证 ——在main.js中,定义全局指令

// 注册一个全局自定义指令 `v-allow`
Vue.directive('allow',{inserted:function(el, binding){// 从vuex中取出points,const points= store.state.user.userInfo.roles.points// 如果points有binding.value则显示if(points.includes(binding.value)){// console.log('判断这个元素是否会显示', el, binding.value)}else{// el.style.display = 'none',这个只是隐藏了,懂业务的通过检查还可以显示,所以要销毁
      el.parentNode.removeChild(el)}}})

使用

<el-button+           v-allow="'import_employee'"
            type="warning"
            size="small"
            @click="$router.push('/import')">导入excel</el-button>

※这里的:'import_employee’是从标识符来的
在这里插入图片描述

  • 作者:勇敢牛牛,冲冲冲
  • 原文链接:https://blog.csdn.net/qq_44613011/article/details/119986608
    更新时间:2022-08-04 08:28:26