Vue3写一个后台管理体系(4)RBAC权限受控体系的实现

藏宝库编辑 2024-9-11 13:34:16 103 0 来自 中国
一、RBAC 权限控制体系

要实现动态Menu,我们必要先来统逐一下认知,明白项目中的权限控制体系。
网上找了张图,我们可以大抵的看下

1.png 从图中,我们可以简单的如许明白RBAC 权限控制体系。

  • 用户:我们登录后台管理体系的账号。举个例子:张三这个人,我们可以以为他是一个用户
  • 脚色:用户的“头衔”。张三是一个贩卖司理,那么“贩卖司理”,我们可以以为他是一个脚色。
  • 权限:每个脚色都有差别的权限。“贩卖司理”这个脚色,可以检察、删除、编辑客户资料,那么张三就可以检察、删除、编辑客户资料,这时间如果有个李四,李四是平凡的“贩卖”的脚色,而平凡的“贩卖”只能检察客户信息,不能删除、编辑客户信息,以是李四只能检察客户信息。
那么明白好了 RBAC 的概念之后,接下来我们就可以往复实现我们的辅助业务了,所谓辅助业务详细指的就是:

  • 员工管理(用户列表)

    • 为用户分副脚色

  • 脚色列表

    • 脚色列表展示
    • 为脚色分配权限

  • 权限列表

    • 权限列表展示

我们先直接做好的后台先看看效果,明白下RBAC在我们后台管理体系中的寄义。

2.png
我们从上面两张图中,可以看到,账号(test),是一个“测试-脚色”的脚色,
而测试脚色的只能看到下面的菜单(权限列表)


而如果我们用超管的账号登录进去,是能看到全部的菜单(权限列表)的

那么由此出现我们可以看出,整个权限体系实在分成了两部门:

  • 页面权限:根据差别的 权限数据,展示差别的页面(就是展示差别的菜单Menu,由于一个菜单按钮,是对应一个详细的页面)
  • 功能权限:根据差别的 权限数据,一个页面里展示差别的 功能按钮
二、下面我们说下代码实现的逻辑


  • 页面权限实现的核心在于 路由表设置
  • 路由表设置的核心在于 私有路由表 privateRoutes
  • 私有路由表 privateRoutes 的核心在于 addRoute API
那么简单一句话总结,我们只必要:根据差别的权限数据,使用 addRoute API 天生差别的私有路由表 即可实现 页面权限 功能
而*实现功能权限的核心在于 根据数据潜伏功能按钮,那么潜伏的方式我们可以通过Vue的指令举行控制
三、页面权限代码实现

起首我们的路由表必要分成公有路由表私有路由表

  • 私有路由表:就是差别脚色拥有差别的路由表
  • 共有路由表:就是每个脚色都有的路由表:比方登录界面、404界面、401界面
    讲清了这些下面实现起来也是很简单的,只是一些细节大概要注意,那么直接看代码吧,代码里都有表明
  • 创建每一个私有路由表


此中一个路由表的代码,其他都是雷同的,要注意的是每个路由表的path是要反面服务端返回的path雷同的,我们到时间是根据路由的path去筛选数据的,这里我用到的全部界面都是test-page页面,但不影响详细大逻辑,大家明白就行
const RightRouter = {  path: '/manage',  component: Layout,  redirect: '/manage/manageList',  alwaysShow: true, // will always show the root menu  name: 'manage',  meta: {    title: '管理1',    icon: 'el-icon-s-check'  },  children: [    {      path: '/manage/manageList',      component: () => import('@/views/test-page/index.vue'),      name: 'list1',      meta: { title: '列表1' }    },    {      path: '/manage/manageList2',      component: () => import('@/views/test-page/index.vue'),      name: 'rightSetList',      meta: { title: '列表2' }    }  ]}export default RightRouter

  • 把每个路由表归并到privateRoutes 中
/** * 私有路由表 */export var privateRoutes = [  permissions,  manageList,]/** * 公开路由表 */export var publicRoutes = [  {    path: '/login',    component: () => import('@/views/login/index')  },  {    path: '/',    // 注意:带有路径“/”的记录中的组件“默认”是一个不返回 Promise 的函数    component: layout,    redirect: '/home',    children: [      {        path: '/home',        name: 'home',        component: () => import('@/views/home/index'),        meta: {title: '首页', affix: true},//affix=true,tagViews右侧没有关闭按钮        hidden: true,//不表现在侧边栏      },      {        path: '/404',        name: '404',        component: () => import('@/views/error-page/404')      },      {        path: '/401',        name: '401',        component: () => import('@/views/error-page/401')      }    ]  }]const router = createRouter({  history: createWebHashHistory(),  // routes: [...publicRoutes, ...privateRoutes]  routes: publicRoutes})export default router我们先看下接口返回的数据


从接口返回的数据中我们能可以看出,一级菜单和二级菜单都是有一个url字段的,我们就是要根据这个url字段和我蹊径由表的path字段去做对表,如果存在,就渲染这个路由,不存在就不去渲染这个路由,以是我们必要先将服务端返回的路由数据,转化成这个格式的数据


筛选路由的详细方法代码
/** * 根据服务端返回的路由数据,筛选过滤当地的路由数据 * @param routes asyncRoutes 当地写的数据 * @param roles 接口获取的数据 */export function filterPrivateRoutes(routes, roles) {  const res = []  routes.forEach(route => {    const tmp = { ...route }    //查抄是否符合权限规则:根据自己公司界说的规则    if (hasPermission(roles, tmp)) {      if (tmp.children) {        tmp.children = filterPrivateRoutes(tmp.children, roles)      }      res.push(tmp)    }  })  return res}export default {  namespaced: true,  state: {    // 路由表:初始拥有静态路由权限    routes: publicRoutes  },  mutations: {    /**     * 增长路由     */    setRoutes(state, newRoutes) {      // 永世在静态路由的底子上增长新路由      state.routes = [...publicRoutes, ...newRoutes]    }  },  actions: {}末了,在在 src/permission 中,获取路由数据之后调用这些代码,干系表明都写到代码里了
// 白名单const whiteList = ['/login']/** * 路由前置保卫 */router.beforeEach(async (to, from, next) => {       ....................        const {roles} = await store.dispatch('user/getPermissionData')        // 处置处罚用户权限,筛选出必要添加的权限        const accessRoutes = await store.dispatch('permission/generateRoutes', roles)        console.log("筛选出必要addRoute的路由",accessRoutes)        // 使用 addRoute 循环添加        accessRoutes.forEach(item => {          router.addRoute(item)        })        // router.addRoutes(accessRoutes)          // hack method to ensure that addRoutes is complete          // set the replace: true, so the navigation will not leave a history record        next({...to, replace: true})    ........................到这里动态菜单差不多就讲完了,但另有一个题目,就是如果我们更换和账户的登录,只有手动革新下页面,左边菜单才会改变,不会自动去改变。这是由于我们退出的时间,没有重置路由表。以是我们在退出的时间,重置下就行了
/** * 重置路由表 */export function resetRouter() {  if (store.getters.hasRoles) {    const menus = store.getters.roles    //removeRoute是根据路由的name去删除路由的,以是我们要对路由的名字举行截取    // const menus = ['getRoleList','admintorList','adminAuth']    // console.log("menus==",menus)    // console.log("router==",router.getRoutes())    menus.forEach(menu => {      let url = menu.url      let i = url.lastIndexOf('/')      let name = url.substring(i+1,url.length)      router.removeRoute(name)    })  }}import router, { resetRouter } from '@/router'logout(context) {      resetRouter()      ...    }四、功能权限代码实现

以是起首我们先去创建如许一个指令(vue3 自界说指令)

  • 我们渴望终极可以通过如许格式的指令举行功能受控 v-permission="'/adminAuth/admintorList'"
  • 以此创建对应的自界说指令 directives/permission
import store from '@/store'import {lowerCase} from '@/utils/index'function checkPermission(el, binding) {  // 获取绑定的值,此处为权限  const value = lowerCase(binding.value);  const auths = store.getters.buttons || [];  if (!auths.includes(value)) {    el.parentNode.removeChild(el);  }}export default {  // 在绑定元素的父组件被挂载后调用  mounted(el, binding) {    checkPermission(el, binding)  },  // 在包含组件的 VNode 及其子组件的 VNode 更新后调用  update(el, binding) {    checkPermission(el, binding)  }}3.在 directives/index 中绑定该指令
import permission from './permission'export default app => {  app.directive('permission', permission)}4.在页面中,添加指令
<el-button type="primary" @click="searchEvent"  v-permission="'/adminAuth/admintorList'">查询</el-button>五、总结

那么到这里我们整个权限受控就算是全部完成了。
整个这一大节中,核心就是 RBAC的权限受控体系 。围绕着 用户->脚色->权限 的体系是如今在包含权限控制的体系中使用率最广的一种方式。
那么怎么针对于权限控制的方案而言,除了文中提到的这种方案之外,实在另有很多其他的方案,大家可以在我们的话题讨论中踊跃发言,多多讨论。
您需要登录后才可以回帖 登录 | 立即注册

Powered by CangBaoKu v1.0 小黑屋藏宝库It社区( 冀ICP备14008649号 )

GMT+8, 2024-10-18 20:22, Processed in 0.168763 second(s), 35 queries.© 2003-2025 cbk Team.

快速回复 返回顶部 返回列表