RBAC权限详解

2023-10-30

1.传统的权限设计

首先,我们先了解下什么是传统的权限设计

在这里插入图片描述

从上面的图中,我们发现,传统的权限设计是对每个人进行单独的权限设置,但这种方式已经不适合目前企业的高效管控权限的发展需求,因为每个人都要单独去设置权限 

2. RBAC权限详解

基于此,RBAC的权限模型就应运而生了,RBAC(Role-Based Access control) ,也就是基于角色的权限分配解决方案,相对于传统方案,RBAC提供了中间层Role(角色),其权限模式如下

 RBAC实现了用户和权限点的分离,想对某个用户设置权限,只需要对该用户设置相应的角色即可,而该角色就拥有了对应的权限,这样一来,权限的分配和设计就做到了极简,高效,当想对用户收回权限时,只需要收回角色即可,接下来,我们就在该项目中实施这一设想

3. 权限设置

1.权限点

权限:在一个系统内是否具有做某个操作的权利
权限分为两个级别

            1. 菜单权限:是否有权限访问某个菜单
            2. 按钮权限:是否有权限操作 页面上的某个按钮功能

2.业务逻辑

对于权限数据来说,有两个级别的设置

1.能不能访问谋个页面

2.在页面上,能不能操作某个按钮

3.RBAC权限设计思想

目标:不同账号登录系统后看到不同的页面, 能执行不同的功能

模式如下

三个关键点:

用户: 就是使用系统的人

权限点:这个系统中有多少个功能(例始:有3个页面,每个页面上的有不同的操作)

角色:不同的权限点的集合

 

实际上就是

  1. 给用户分配角色
  2. 给角色分配权限点

实际业务中:

  1. .先给员工分配一个具体的角色
  2. 然后给角色分配具体的权限点 (工资页面 工资页面下的操作按钮)员工就拥有了权限点

4.权限具体业务

在这里插入图片描述

在这里插入图片描述

4. 具体步骤

1.动态生成左侧菜单-addRoutes方法

在router中直接静态写死的动态路由表改造成通过addRoutes 方法调用添加的形式

// 引入所有的动态路由表(未经过筛选)
import router, { asyncRoutes } from '@/router'
const whiteList = ['/login', '/404']
router.beforeEach(async(to, from, next) => {
  NProgress.start() // 启动进度条
  if (store.getters.token) {
    if (to.path === '/login') {
      next('/')
    } else {
      if (!store.getters.userId) {
        // 判断userInfo有没有id值,如果没有就进user/getUserInfo
        const menus = await store.dispatch('user/getUserInfo')
        console.log('当前用户可以访问的权限是', menus)
        // 根据用户的实际权限menus,可以在asyncRoutes筛选出用户可以访问的权限
        const filterRoute = asyncRoutes.filter(route => {
          return menus.includes(route.children[0].name)
        })
 
        // 因为404页面在路由的中间位置,要进去之前404路由后面的路由时,直接进404页面了
        // 把404路由添加到所有路由的末尾就可以解决这个问题
        filterRoute.push( // 404 page must be placed at the end !!!
          { path: '*', redirect: '/404', hidden: true })
        // 改写成动态添加路由
        // addRoutes用来动态添加路由配置
        // 只有在这里设置了补充路由配置,才能去访问页面,如果没有设置的话,左边的菜单不显示的
        router.addRoutes(filterRoute)
 
        // 把他们保存到vuex中,在src\layout\components\Sidebar\index.vue
        // 生成左侧菜单时,也应该去vuex中拿
        store.commit('menu/setMenuList', filterRoute)
        // 解决刷新出现的白屏bug
        next({
          ...to, // next({ ...to })的目的,是保证路由添加完了再进入页面 (可以理解为重进一次)
          replace: true // 重进一次, 不保留重复历史
        })
      } else {
        next()
      }
    }
  } else {
    if (whiteList.includes(to.path)) {
      next()
    } else {
      next('/login')
    }
  }
})

我们发现左侧的菜单只剩下静态的首页了,浏览器手动输入某一个动态路由地址,依旧是可用的,这证明我们其实已经把动态路由添加到我们的路由系统了。

2.动态生成左侧菜单-改写菜单保存位置

当前的菜单渲染使用的数据:this.r o u t e r . o p t i o n s . r o u t e s 这 个 数 据 是 固 定 , a d d R o u t e s 添 加 的 路 由 表 只 存 在 内 存 中 , 并 不 会 改 变 t h i s . router.options.routes 这个数据是固定,addRoutes添加的路由表只存在内存中,并不会改变this.router.options.routes这个数据是固定,addRoutes添加的路由表只存在内存中,并不会改变this.router.options.routes

目标:调用router.addRoutes() , 想要数据反映到视图上, 将路由信息存在vuex中

在这里插入图片描述

2.1定义vuex管理菜单数据

在src/store/modules下补充menu.js :

 

// 导入静态路由
import { constantRoutes } from '@/router'
export default {
  namespaced: true,
  state: {
    // 先以静态路由作为菜单数据的初始值
    menuList: [...constantRoutes]
  },
  mutations: {
    setMenuList(state, asyncRoutes) {
      // 将动态路由和静态路由组合起来
      state.menuList = [...constantRoutes, ...asyncRoutes]
    }
  }
}

src/store/index.js中注册这个模块

2.2提交setMenuList生成完整的菜单数据

修改src/permission.js:

if (!store.getters.userId) {
    await store.dispatch('user/getUserInfo')
    // 把动态路由数据交给菜单
    store.commit('menu/setMenuList', asyncRoutes)
    // 把动态路由添加到应用的路由系统里
    router.addRoutes(asyncRoutes)
}

2.3菜单生成部分改写使用vuex中的数据

routes() {
  // 拿到的是一个完整的包含了静态路由和动态路由的数据结构
  // return this.$router.options.routes
  return this.$store.state.routeMenu.menuList
}

3.使用权限数据做过滤处理

  1. 通过后台返回的权限数据, 过滤出要显示的菜单, 过滤使用路由的name作为标识
  2. 从action中获取返回值
    action本质上是一个promise 它的return 结果可以通过const res = await action名来接收

在这里插入图片描述

3.store/modules/user.js

 

// 用来获取用户信息的action
    async getUserInfo(context) {
      // 1. ajax获取基本信息,包含用户id
      const rs = await getUserInfoApi()
      console.log('用来获取用户信息的,', rs)
      // 2. 根据用户id(rs.data.userId)再发请求,获取详情(包含头像)
      const info = await getUserDetailById(rs.data.userId)
      console.log('获取详情', info.data)
      // 把上边获取的两份合并在一起,保存到vuex中
      context.commit('setUserInfo', { ...info.data, ...rs.data })
      return rs.data.roles.menus
    },

4.在permission.js中过滤

if (!store.getters.userId) {
        // 有token,要去的不是login,就直接放行
        // 进一步获取用户信息
        // 发ajax---派发action来做
        const menus = await store.dispatch('user/getUserInfo')
        console.log('当前用户能访问的页面', menus)
        console.log('当前系统功能中提供的所有的动态路由页面是', asyncRoutes)
        // 根据本用户实际的权限menus去 asyncRoutes 中做过滤,选出本用户能访问的页面
 
        const filterRoutes = asyncRoutes.filter(route => {
          const routeName = route.children[0].name
          return menus.includes(routeName)
        })
 
        // 一定要在进入主页之前去获取用户信息
 
        // addRoutes用来动态添加路由配置
        // 只有在这里设置了补充了路由配置,才可能去访问页面
        // 它们不会出现左侧
        router.addRoutes(filterRoutes)
 
        // 把它们保存在vuex中,在src\layout\components\Sidebar\index.vue
        // 生成左侧菜单时,也应该去vuex中拿
        store.commit('menu/setMenuList', filterRoutes)
 
        // 解决刷新出现的白屏bug
        next({
          ...to, // next({ ...to })的目的,是保证路由添加完了再进入页面 (可以理解为重进一次)
          replace: true // 重进一次, 不保留重复历史
        })
      }

注意事项

  • 解决404问题
    原因:现在我们的路由设置中的404页处在中间位置而不是所有路由的末尾了
    解决办法:把404页改到路由配置的最末尾就可以了
    \1. 从route/index.js中的静态路由中删除path:’*'这一项
    \2. 在permission.js中补充在最后

代码示例

    if (!store.getters.userId) {
        // 有token,要去的不是login,就直接放行
        // 进一步获取用户信息
        // 发ajax---派发action来做
        const menus = await store.dispatch('user/getUserInfo')
        console.log('当前用户能访问的页面', menus)
        console.log('当前系统功能中提供的所有的动态路由页面是', asyncRoutes)
        // 根据本用户实际的权限menus去 asyncRoutes 中做过滤,选出本用户能访问的页面
 
        const filterRoutes = asyncRoutes.filter(route => {
          const routeName = route.children[0].name
          return menus.includes(routeName)
        })
 
        // 一定要在进入主页之前去获取用户信息
 
        // 把404加到最后一条
        filterRoutes.push( // 404 page must be placed at the end !!!
          { path: '*', redirect: '/404', hidden: true })
 
        // addRoutes用来动态添加路由配置
        // 只有在这里设置了补充了路由配置,才可能去访问页面
        // 它们不会出现左侧
        router.addRoutes(filterRoutes)
 
        // 把它们保存在vuex中,在src\layout\components\Sidebar\index.vue
        // 生成左侧菜单时,也应该去vuex中拿
        store.commit('menu/setMenuList', filterRoutes)
 
        // 解决刷新出现的白屏bug
        next({
          ...to, // next({ ...to })的目的,是保证路由添加完了再进入页面 (可以理解为重进一次)
          replace: true // 重进一次, 不保留重复历史
        })
      } 
  • 退出时重置路由

router/index.js中

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

这个方法就是将路由重新实例化,相当于换了一个新的路由,之前**加的路由就不存在了,需要在登出的时候, 调用一下即可**

import { resetRouter } from '@/router'
// 退出的action操作
logout(context) {
  // 1. 移除vuex个人信息
  context.commit('removeUserInfo')
  // 2. 移除token信息
  context.commit('removeToken')
  // 3. 重置路由
  resetRouter()
  // 4. 重置 vuex 中的路由信息 只保留每个用户都一样的静态路由数据
  //    在moudules中的一个module中去调用另一个modules中的mutation要加{root:true}
  context.commit('setMenuList', [], { root: true })
}

4.控制操作按钮

在这里插入图片描述

定义全局检测的方法:

Vue.prototype.$checkPoint = function(pointKey) {
  if (store.state.user.userInfo.roles.points) {
    // 进行权限点判断
    return store.state.user.userInfo.roles.points.includes(pointKey)
  }
  // 没有权限点POINTS信息, 说明用户没有身份, 没有任何权限
  return false

在模板中通过if来控制按钮显示

<template>
  <div class="dashboard-container">
    <div class="app-container">
      <el-card>
        <el-button v-if="$checkPoint('CKGZ')">查看工资</el-button>
      </el-card>
    </div>
  </div>
</template>

$checkPoint中的参数以系统中权限点的标识符为准。

或者自定义指令控制按钮显示

在main.js中定义全局指令

// 注册一个全局自定义指令 `v-allow`
Vue.directive('allow', {
  // 当被绑定的元素插入到 DOM 中时……
  inserted: function(el, binding) {
    // v-focus="'abc'"  ===> binding.value = 'abc'
    if (store.state.user.userInfo.roles.points.includes(binding.value)) {
      // 元素是可见的
    } else {
      el.style.display = 'none'
    }
  }

使用

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

 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

RBAC权限详解 的相关文章

  • 在 javascript/jquery 中将光标更改为等待

    当调用函数时 如何让光标更改为此加载图标以及如何将其更改回 javascript jquery 中的普通光标 在你的 jQuery 中使用 body css cursor progress 然后又恢复正常 body css cursor d
  • 了解设置 JQuery 变量

    了解设置 JQuery 变量 最近 我通过在 StackOverflow 上遇到的另一个问题寻找帮助 了解到如何设置 JQuery 变量 如下所示 您可以通过简单地调用变量来创建输入字段 并且锚变量似乎也定义了样式 var clicked
  • 从数据 URI 解码 QR 码

    我尝试从数据 uri 中解码二维码 var dataUri data image gif base64 R0lGODdh9gD2AIAAAAAAAP ywAAAAA9gD2AAAC decodeQrCode dataUri cb 我已经尝试
  • 不和谐机器人 |不和谐.js |类型错误:无法读取未定义的属性“长度”

    我正在制作一个 Discord 机器人 并且正在使用 CodeLyon 的视频作为参考 该错误位于我的 message js 文件中 该文件包含以下内容 require dotenv config create cooldowns map
  • 从未用 @flow 标记的导入文件中获取类型定义

    TL DR我怎么告诉flow从未声明的导入模块导入类型定义 flow 加长版 流接缝能够从不使用流语法的文件中派生类型 请参阅示例 示例文件 flow js if Math random lt 0 5 var y hello else va
  • 标签获取 href 值

    我有以下 html div class threeimages a img alt Australia src Images Services 20button tcm7 9688 gif a div class text h2 a hre
  • 使用 KnockoutJs 映射插件进行递归模板化

    我正在尝试使用以下方法在树上进行递归模板化ko映射 插入 http knockoutjs com documentation plugins mapping html 但我无法渲染它 除非我定义separate每个级别的模板 在以下情况下
  • 如何使输入字段和提交按钮变灰

    我想变灰这两件事 http doorsplit heroku com 歌曲输入字段和提交按钮 直到用户输入艺术家 有没有一种简单的方法可以通过 JQuery 来做到这一点 艺术家输入字段的id是 request artist 你可以这样做
  • Firefox 书签探索未超过 Javascript 的第一级

    我已经编写了一些代码来探索我的 Firefox 书签 但我只获得了第一级书签 即我没有获得文件夹中的链接 e g 搜索引擎 雅虎网站 谷歌网站 在此示例中 我只能访问 Search engines 和 google com 不能访问 yah
  • 提交表单并重定向页面

    我在 SO 上看到了很多与此相关的其他问题 但没有一个对我有用 我正在尝试提交POST表单 然后将用户重定向到另一个页面 但我无法同时实现这两种情况 我可以获取重定向或帖子 但不能同时获取两者 这是我现在所拥有的
  • Laravel 中只向登录用户显示按钮

    如果我以 John 身份登录 如何才能只显示 John 的红色按钮而不显示 Susan 的红色按钮 测试系统环境 Win10 Laravel5 4 Mysql5 7 19 table class table table responsive
  • 为什么在 Internet Explorer 中访问 localStorage 对象会引发错误?

    我正在解决一个客户端问题 Modernizr 意外地没有检测到对localStorageInternet Explorer 9 中的对象 我的页面正确使用 HTML 5 文档类型 并且开发人员工具报告该页面具有 IE9 的浏览器模式和 IE
  • FireFox 中的自动滚动

    我的应用程序是实时聊天 我有一个 Div 来包装消息 每条消息都是一个 div 所以 在几条消息之后 我的 DOM 看起来像这样 div div Message number two div div div div
  • 如何在类似控制台的环境中运行 JavaScript?

    我正在尝试遵循这里的示例 http eloquentjavascript net chapter2 html http eloquentjavascript net chapter2 html and print blah 在浏览器中运行时
  • 模块构建失败(来自 ./node_modules/babel-loader/lib/index.js)Vue Js

    我从 GitHub 下载了一个我和我的朋友正在开发的项目 但是当我尝试运行时 npm run serve 我收到这个错误 src main js 中的错误 Module build failed from node modules babe
  • 如何获取给定 DOM 元素的所有定义的 CSS 选择器?

    如何使用 jQuery 获取给定 DOM 元素的所有定义的 CSS 选择器 定义后 我的意思是在应用于任何样式表的所有 CSS 选择器document 在某种程度上 这类似于 FireBug 实现的功能 其中显示所选 DOM 元素的所有应用
  • Javascript转换时区问题

    我在转换当前时区的日期时间时遇到问题 我从服务器收到此日期字符串 格式为 2015 10 09T08 00 00 这是中部时间 但是当我使用 GMT 5 中的 new Date strDate 转换此日期时间时 它返回给我的信息如下 这是不
  • 在 React.js 中编辑丰富的数据结构

    我正在尝试为数据结构创建一个简单的基于网格的编辑器 但我在使用 React js 时遇到了一些概念问题 他们的文档对此没有太大帮助 所以我希望这里有人可以提供帮助 首先 将状态从外部组件传输到内部组件的正确方法是什么 是否有可能将内部组件中
  • 使用 Ajax 请求作为源数据的 Jquery 自动完成搜索

    我想做的事 我想使用 jquery 自动完成函数创建一个输入文本字段 该函数从跨域curl 请求获取源数据 结果应该与此示例完全相同 CSS 在这里并不重要 http abload de img jquerydblf5 png http a
  • 如何从图像输入中获取 xy 坐标?

    我有一个输入设置为图像类型

随机推荐

  • 统计从键盘输入一行字符的个数.c

    统计从键盘输入一行字符的个数 一个字符代表一个 一个汉字代表两个 思想 当输入的字符不等于键盘上的enter键时 每输入一个字符就加1 include
  • Java基础 :HashSet(使用方法详解)

    Java HashSet HashSet 基于 HashMap 来实现的 是一个不允许有重复元素的集合 HashSet 允许有 null 值 HashSet 是无序的 即不会记录插入的顺序 HashSet 不是线程安全的 如果多个线程尝试同
  • ORA-01795: 列表中的最大表达式数为1000的解决方法

    ORA 01795 列表中的最大表达式数为1000的解决方法 IN中的数据量不能超过1000条 解决方案 把条件分成多个少于1000的IN即 DELETE FROM T MM SECTION SITE UPDATE WHERE T T MM
  • python绘图点样式

    plt plot x y x markersize 3 linestyle color darkgreen
  • unity体感游戏--接钻石游戏(三)游戏物体碰撞得分

    u3d的碰撞函数是OnTriggerEnter 代码如下 using UnityEngine using System Collections public class onCollider MonoBehaviour public Gam
  • 阿里云ECS服务器使用教程 新手上云好助手

    随着普及率越来越高 云服务器已经成为企业及个人用户开展网络业务的首选了 阿里云服务器在国内起步早 现在的用户数量是国内第一 全球五强 因为初次接触云服务 所以阿里云服务器的使用方法 对于很多新手小白还不太了解 下面老魏就讲解阿里云服务器的简
  • 多种方式解决Java控制台报错 java.util.LinkedHashMap cannot be cast to.....

    问题描述 今天在使用RestTemplate调用服务的时候 因为服务提供者返回的是一个List集合 所以在使用消费者调用的时候 restTemplate getForObject 期待返回的类型直接写成了List class 相关代码如下
  • 写5个数学建模的经典模型案例和代码

    1 线性规划模型案例 生产计划 假设一家工厂生产两种产品A和B 每个月有100个工作日 每个工作日可以生产200个A产品或150个B产品 A产品售价为200元 个 B产品售价为300元 个 每个月至少需要保证收入不低于200000元 制定生
  • 随机效应估算与固定效应估算_固定效应还是随机效应——Hausman检验.PPT

    固定效应还是随机效应 Hausman检验 7 3 随机效应模型估计 7 3 2 用EViews7 2估计随机效应模型 数据导入 数据结构转换以及模型设定与固定效应模型估计一样 不同的是在panel option的cross section中
  • Vue之父子组件通过事件通信

    前言 组件间传值的章节我们知道父组件给子组件传值的时候 使用v bind的方式定义一个属性传值 子组件根据这个属性名去接收父组件的值 但是假如子组件想给父组件一些反馈呢 就不能使用这种方式来 而是使用事件的方式 父组件通过注册这个事件的监听
  • 使用Docker安装Mysql数据库,及国内常用docker镜像地址

    1 安装docker 输入 yum install y docker 2 配置docker镜像地址 输入 vi etc docker daemon json 在配置文件中写入 registry mirrors http hub mirror
  • 拷贝复制命令行输出放在系统剪贴板上

    转载自 http oldratlee com post 2012 12 23 command output to clip 为什么要这么做 直接把命令的输出 比如 grep awk sed find 或是你的程序输出结果 放到剪切板上 这么
  • Redis使用总结(三、缓存击穿问题)

    什么是缓存击穿 在谈论缓存击穿之前 我们先来回忆下从缓存中加载数据的逻辑 如下图所示 因此 如果黑客每次故意查询一个在缓存内必然不存在的数据 导致每次请求都要去存储层去查询 这样缓存就失去了意义 如果在大流量下数据库可能挂掉 这就是缓存击穿
  • 低功耗设计之门控时钟

    门控时钟一般是用于多比特的数据 因为门控单元也有资源消耗 需要耗电 一般数据位宽超过3bit才会有收益 1 与门 或门 门控时钟 门控时钟的控制信号可以选用高电平有效或低电平有效 若控制信号高电平有效 可以使用与门进行时钟门控 时钟被关闭时
  • Qt 子窗口和父窗口,子窗口和子窗口控件获取

    文章目录 前言 一 代码 二 局限性 前言 Qt开发过程中 难免会遇到子窗口需要获取父窗口某个控件的状态 或者是子窗口需要获取另外一个子窗口某个控件的状态 之前用过回调 全局变量 信号和槽连接 但是都太麻烦了 后面研究出一种简单的方法 会有
  • A--玉米大炮--2022河南萌新联赛第(三)场:河南大学

    输入 3 3 1 1 2 2 3 3 输出 0 说明 开始时 小蓝控制所有大炮立即发射炮弹 僵王博士受到 666 点伤害 直接被击溃 示例2 输入 2 20 5 1 5 3 输出 2 说明 开始时 小蓝控制所有大炮立即发射炮弹 僵王博士受到
  • Linux终端配置——zsh+插件优化

    文章目录 安装Zsh Ubuntu版本 安装插件 更改默认shell 并配置插件和主题 启动插件和主题 刚入Linux的萌新 发现Linux虽然牛 但是这个终端也太不智能了 既不美观也不智能 所以本篇博客可以通过安装zsh进行简单的优化 包
  • react16常见api以及原理剖析

    Vue 与 React 两个框架的粗略区别对比 Vue 的优势包括 模板和渲染函数的弹性选择 简单的语法及项目创建 更快的渲染速度和更小的体积 React 的优势包括 更适用于大型应用和更好的可测试性 同时适用于 Web 端和原生 App
  • sqli练习1-5关(超详细)

    此博客仅用来记录我的学习过程 我会把每一步都记录下来 sqlli 练习 第一关 手工注入 第二关 手工注入 第三关 手工注入 第四关 手工注入 第五关 手工注入 第一关 手工注入 判断是否存在注入 第二关 手工注入 判断是否存在注入以及注入
  • RBAC权限详解

    1 传统的权限设计 首先 我们先了解下什么是传统的权限设计 从上面的图中 我们发现 传统的权限设计是对每个人进行单独的权限设置 但这种方式已经不适合目前企业的高效管控权限的发展需求 因为每个人都要单独去设置权限 2 RBAC权限详解 基于此