RBAC权限详解

news2024/11/13 15:43:46

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>

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/812076.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

大盗阿福(记忆化搜索板子)

提供核心代码&#xff1a;&#xff08;经典的记忆化搜索套路&#xff09; int dfs(int pos){if(f[pos]!-1) return f[pos];//记忆化if(pos>n) return 0;//边界&#xff0c;越界int sum0;//模板int f10,f20;f1dfs(pos1);f2dfs(pos2)w[pos];summax(f1,f2);//模板f[pos]sum;//模…

表达式树

请设计一个算法&#xff0c;将给定的表达式树(二叉树)转换为等价的中缀表达式(通过括号反映操作符的计算次序)并输出。 例如&#xff0c;当下列两棵表达式树作为算法的输入时&#xff1a; 输出的等价中缀表达式分别为 (ab)*(c*(-d)) 和 (a*b)(-(c-d))。 注意&#xff1a; 树…

ChatGPT炒股:自动批量提取股票公告中的表格并合并数据

在很多个股票公告中&#xff0c;都有同样格式的“日常性关联交易”的表格&#xff0c;如何合并到一张Excel表格中呢&#xff1f; 首先&#xff0c;在ChatGPT中输入提示词&#xff1a; 写一段Python代码&#xff1a; F盘文件夹“新三板 2023年日常性关联交易20230704”中很多…

零代码编程:PDF文件名和Excel数据进行比对找不同

F盘“北交所招股说明书”文件夹下有150个文件&#xff1b; F盘”北证A股20230703.xlsx”表格中证券名称有200多个&#xff1b; 现在想找出文件夹下的哪些证券名称不在表格里面。 在ChatGPT中输入提示词&#xff1a; 写一段Python程序&#xff1a; F盘“北交所招股说明书”文…

qt源码--事件系统之QAbstractEventDispatcher

1、QAbstractEventDispatcher内容较少&#xff0c;其主要是定义了一些注册接口&#xff0c;如定时器事件、socket事件、注册本地事件、自定义事件等等。其源码如下&#xff1a; 其主要定义了大量的纯虚函数&#xff0c;具体的实现会根据不同的系统平台&#xff0c;实现对应的方…

AD21原理图的高级应用(五)自定义原理图模板及调用

&#xff08;五&#xff09;自定义原理图模板及调用 1.创建原理图模板2.调用原理图模板 1.创建原理图模板 利用 Altium Designer 软件在原理图中创建自己的模板,可以在图纸的右下角绘制一个表格用于显示图纸的一些参数,例如文件名、作者、修改时间、审核者、公司信息、图纸总数…

建造者设计模式 + 高阶函数 => DSL

该设计模式适用于创建复杂对象&#xff0c;该复杂对象通常是由各个部分的子对象用一定的算法或者步骤构成&#xff0c;针对每个子对象内部算法和步骤通常是稳定的&#xff0c;但是该复杂对象的确实由于不同的需求而选择使用不同的子对象进行组装。对于构建该复杂的对象&#xf…

Vue2 第六节 key的作用与原理

&#xff08;1&#xff09;虚拟DOM &#xff08;2&#xff09;v-for中的key的作用 一.虚拟DOM 1.虚拟DOM就是内存中的数据 2.原生的JS没有虚拟DOM: 如果新的数据和原来的数据有重复数据&#xff0c;不会在原来的基础上新加数据&#xff0c;而是重新生成一份 3. Vue会有虚拟…

结构方程模型的绘制

模型的绘制是我们最终呈现出的一个结果 所以说这个课程主要关注的有两点 第一点就是模型图的绘制 第二点就是结果的解释 关于中间计算过程和背后的理论的一个结果 在本文章的所有的讲解过程中 只注重模型图的绘制方法 如何高效的绘制出所需要的一个模型图 同时能够调整…

【小吉带你学Git】Git分支

&#x1f38a;专栏【Git】 &#x1f354;喜欢的诗句&#xff1a;更喜岷山千里雪 三军过后尽开颜。 &#x1f386;音乐分享【Counting Stars 】 欢迎并且感谢大家指出小吉的问题&#x1f970; 文章目录 &#x1f916;概述&#x1f354;什么是分支&#x1f354;使用分支的好处&am…

百万QPS系统如何设计?

一、关系链业务简介 从主站业务角度来看&#xff0c;关系链指的是用户A与用户B的关注关系。以关注属性细分&#xff0c;以关注&#xff08;订阅&#xff09;为主&#xff0c;还涉及拉黑、悄悄关注、互相关注、特别关注等多种属性或状态。目前主站关系链量级较大&#xff0c;且还…

九、HAL_IWDG独立看门狗的使用

1、开发环境 (1)Keil MDK: V5.38.0.0 (2)STM32CubeMX: V6.8.1 (3)MCU: STM32F407ZGT6 2、IWDG简介 (1)IWDG即独立看门狗。 (2)看门狗本质上是一个定时器&#xff0c;设置一个时间&#xff0c;时间到即让程序复位。所以需要在在时间未到之前重置定时器&#xff0c;也就是喂…

线性表详细讲解

2.1 线性表的定义和特点2.2 案例引入2.3 线程表的类型定义2.4 线性表的顺序表示和实现2.4.1 线性表的顺序存储表示2.4.2 线性表的结构类型定义2.4.3 顺序表基本操作的实现2.4.4 顺序表总结 2.5 线性表的链式表示和实现2.5.1 线性表的链式存储表示2.5.2 单链表的实现&#xff08…

ARM裸机-3

1、嵌入式和单片机的区别 1.1、芯片平台 主流的单片机平台&#xff1a;51、PIC、STM32、AVR、MSP430等 主流的嵌入式平台&#xff1a;ARM、PPC、MIPS 1.2、资源、价格、应用领域 单片机片上资源有限、价格低、应用领域多为小家电、终端设备等。 嵌入式系统片上资源丰富、价格…

数据库连接及使用Statement对象完成CRUD

一、数据库连接&#xff1a; 二、使用Statement对象完成CRUD&#xff1a; 1、插入&#xff1a; 2、删除 3、修改 4、查询 三、ORM对象关系映射

数据结构:顺序表详解

数据结构&#xff1a;顺序表详解 一、 线性表二、 顺序表概念及结构1. 静态顺序表&#xff1a;使用定长数组存储元素。2. 动态顺序表&#xff1a;使用动态开辟的数组存储。三、接口实现1. 创建2. 初始化3. 扩容4. 打印5. 销毁6. 尾插7. 尾删8. 头插9. 头删10. 插入任意位置数据…

pytorch 中 view 和reshape的区别

在 PyTorch&#xff08;一个流行的深度学习框架&#xff09;中&#xff0c; reshape 和 view 都是用于改变张量&#xff08;tensor&#xff09;形状的方法&#xff0c;但它们在实现方式和使用上有一些区别。下面是它们之间的主要区别&#xff1a; 实现方式&#xff1a; reshap…

13年测试经验,性能测试-压力测试指标分析总结,看这篇就够了...

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 一般推荐&#xf…

Jmeter环境变量配置及测试

上图是Windows版本的测试结果。 Windows系统&#xff1a; win11&#xff1a;“此电脑”——鼠标右键“属性”——“高级系统设置”——“环境变量” 1.1 新建“系统变量”&#xff1a;JMETER_HOME JMETER_HOME变量值为解压后的jmeter路径&#xff0c;如&#xff1a; D:\apach…

AD21原理图的高级应用(三)原理图多通道的应用

&#xff08;三&#xff09;原理图多通道的应用 在很多大型的设计过程中&#xff0c;我们可能会遇到需要重复使用某个图纸&#xff0c;如果使用常规的复制粘贴&#xff0c;虽然可以达到设计要求,但原理图的数量将会变得庞大而烦琐。Altium Designer 支持多通道设计。 多通道设…