RBAC权限控制具体实现

RBAC权限控制具体实现

今天记录一个RBAC权限控制的具体实现方法

  • 每个用户 可分配 若干角色
  • 每个角色 分配 若干功能

超级管理员:

  • 修改每个用户的角色
  • 修改每个角色的功能

页面权限

所谓页面权限包含两部分内容:

  1. 用户可看到的:左侧 menu 菜单的 item 展示
  2. 用户看不到的:路由表配置

我们知道 左侧 menu 菜单是根据路由表自动生成的。 所以以上第一部分的内容其实就是由第二部分引起的。

路由表分成了两部分:

  1. 私有路由表 privateRoutes:依据权限进行动态配置的

  2. 公开路由表 publicRoutes:无权限要求的

    创建路由表时,只导入公开路由表,私有路由表后续通过 addRoute动态导入

TypeScript 复制代码
export const privateRoutes = [...]
export const publicRoutes = [...]

const router = createRouter({
  history: createWebHashHistory(),
  routes: publicRoutes
})

那么想要实现 页面权限 核心的点就是在 **私有路由表 **privateRoutes

我们期望的是:不同的权限进入系统可以看到不同的路由 。那么换句话而言是不是就是:根据不同的权限数据,生成不同的私有路由表?

对于 vue-router 4 而言,提供了 addRoute API ,可以 动态添加路由到路由表中 ,那么我们就可以利用这个 API 生成不同的路由表数据。

TypeScript 复制代码
/**
 * 根据权限筛选路由
 */
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
}
TypeScript 复制代码
// 判断用户资料是否获取
// 若不存在用户信息,则需要获取用户信息
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. 路由表配置的核心在于 **私有路由表 **privateRoutes
  3. 私有路由表 privateRoutes 的核心在于 addRoute API

那么简单一句话总结,我们只需要:**根据不同的权限数据,利用 addRoute API 生成不同的私有路由表 ** 即可实现 页面权限 功能

页面权限实现方法:

整个 页面权限 实现分为以下几步:

  1. 从后端获取 权限数据
  2. 私有路由表 不再被直接加入到 routes
  3. 利用 addRoute API 动态添加 与后端权限数据匹配的路由到 路由表

功能权限

功能权限 的难度低于页面权限,所谓功能权限指的只有一点:

  1. 根据不同的 权限数据 ,展示不同的 功能按钮

对于 功能权限 而言,只需要:根据权限数据,隐藏功能按钮 即可

功能权限实现方法:

整个 功能权限 实现分为以下几步:

  1. 获取 权限数据
  2. 定义 隐藏按钮方式(通过vue 的自定义指令)
  3. 依据数据隐藏按钮

所以首先我们先去创建这样一个指令(vue3 自定义指令

  1. 我们期望最终可以通过这样格式的指令进行功能受控 v-permission="['importUser']"
  2. 以此创建对应的自定义指令 directives/permission
TypeScript 复制代码
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)
    })
    // 如果v-permission的数组中没有当前用户的功能权限
    // 则表示当前用户无该指令,那么删除对应的功能按钮
    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. directives/index 中绑定该指令
JavaScript 复制代码
...
import permission from './permission'

export default (app) => {
  ...
  app.directive('permission', permission)
}
  1. 在所有功能中,添加该指令
  2. views/role-list/index
html 复制代码
<el-button
    ...
    v-permission="['distributePermission']"
>
{{ $t('msg.role.assignPermissions') }}
</el-button>

后端是如何存储权限数据的

userinfo表:

  • 这里的roldId存储的是改用户的角色(可以有多个)

role表:

  • 这里的id:1,2,3分别代码不同的角色

user permission表:

  • 这里的id对应role表的角色id,这里存储的是每个角色对应的权限有哪些

  • 纯数字的是页面权限,X-X格式的是功能权限

相关推荐
前端爆冲5 分钟前
项目中无用export的检测方案
前端
旧味清欢|15 分钟前
关注分离(Separation of Concerns)在前端开发中的实践演进:从 XMLHttpRequest 到 Fetch API
javascript·http·es6
热爱编程的小曾33 分钟前
sqli-labs靶场 less 8
前端·数据库·less
gongzemin44 分钟前
React 和 Vue3 在事件传递的区别
前端·vue.js·react.js
Apifox1 小时前
如何在 Apifox 中通过 Runner 运行包含云端数据库连接配置的测试场景
前端·后端·ci/cd
-代号95271 小时前
【JavaScript】十四、轮播图
javascript·css·css3
树上有只程序猿1 小时前
后端思维之高并发处理方案
前端
庸俗今天不摸鱼2 小时前
【万字总结】前端全方位性能优化指南(十)——自适应优化系统、遗传算法调参、Service Worker智能降级方案
前端·性能优化·webassembly
QTX187302 小时前
JavaScript 中的原型链与继承
开发语言·javascript·原型模式
黄毛火烧雪下2 小时前
React Context API 用于在组件树中共享全局状态
前端·javascript·react.js