rbac权限管理

一.概述

传统权限管理:

image-20221101155238501

类似于这样,每新增一个人都要重新给她一些权限,是针对每个人单独设置的,这种方法已经不适用于高效管控权限的

基于此,RBAC权限模型就诞生了,Role-Based Access control也就是基于角色的权限控制,相对于传统模式,这套方案强调一个role角色

image-20221101155927200

RBAC实现了用户和权限点的分离,相对某个用户设置权限,不用再去一个权限一个权限的给他单独设置,而是直接给他设置角色即可,这样权限的分配和设计就达到了极简,高效,当想对用户收回权限时,只需要收回角色即可

二.分配员工角色

2.1 新建角色窗体

应该在点击角色时弹出角色窗体

image-20221101161419809

image-20221101161440246

首先新建一个弹出层dialog的组件

image-20221101161634238

里面标签体dialog的一个居中布局

注意el-row快速变为flex布局,且居中分布

image-20221101162159147

然后我们中间的内容部分用到组件 ,复选框组checkbox-group,中间是我们后面的角色循环每一项

image-20221101162347741

注意这个组件接受两个父组件传过来的值,一个是该dialog显示隐藏的参数,一个是当前点的这个用户的id等信息,我拿到了才能去给这个用户添加角色

image-20221101162546788

2.2 获取角色列表和当前用户角色

首先需要在父组件导入弹出组件并应用在页面中

image-20221101163339564

然后我们首先需要传控制当前弹出显示隐藏的数据

在父组件data定义好一个变量

image-20221101163445697

给子组件传过来

image-20221101163503596

注意我们props是多个单词采用驼峰命名法,也就是props是支持驼峰命名的,但是在用的时候,也就是传过来的值这里要采用首字母全小写,连接线连接的形式

后面加一个.sync的修饰符瞬间形成了自定义事件,方便我们后面子组件点击取消确定等修改他的值

然后我们还要定义一个userId给子组件传过来

image-20221101163927740

image-20221101163940171

给角色按钮添加点击事件,传参当前这一行的id

在这个事件立面展示dialog,将userid赋值即可

image-20221101164038159

然后就应该在子组件这边来操作了

之前在做这个页面的时候,获取过角色列表

image-20221101171110141

image-20221101171218949

按需导入我们这个请求函数

image-20221101171322769

然后再created钩子发起请求

这里注意,我们这个接口默认是返回十条数据,我们就传进去参数20,就假装默认为有20个角色,一般角色也不会超过很多

image-20221101171446325

然后取里面的角色列表给到data

image-20221101171530834

遍历循环我们的复选框每一项

注意这里面的label,我们element组件说的很清楚,这里label既是显示的值,也充当我们收集的值

image-20221101171742225

image-20221101171609914

但是我这里只想收集id,显示name怎么办

他存他的,我显示的内容用一个插值语法来代替即可

image-20221101171830181

然后我们得checkbox-group也要绑定一个数组用来存储我们选择的一些id值

data先定义

image-20221101171951672

image-20221101172018627

然后我们需要将当前用户已经绑定好的一些角色权限一打开就勾选上的状态

image-20221101174239420

这里是这样做的

还是在子组件导入请求函数,并写在方法内

但是我们这里不是在这里调用,是我们父组件一点击角色这个事件里面来调用

这里不能直接用父组件传过来的id作为参数

image-20221101173349399

因为我们这个id是props接受的,一个重要概念,props赋值是异步的,所以我们要一点击角色打开的瞬间就发起请求,这个时候没有这个id

我们可以通过给外层函数接受一个参数,然后我们的接口函数拿这个参数作为id,要知道我们是在父组件来调用

image-20221101173609503

给子组件打标识拿到实例,然后在点击事件调用这个函数

image-20221101173645627

我们每次点开会有一个闪空白的瞬间

解决思路:

其原因是因为我们定义的这个函数因为async和await所以它是一个异步函数,而我们点击事件里面,展示为true是同步操作

image-20221101174510848

所以我们应该让方法调用完执行完了再去展示

那么就可以给我们的这个调取实例的函数调用来一个await 等待他执行完才去执行下面的

image-20221101174554703

2.3 给员工分配角色

定义好保存用户角色的接口函数

image-20221101174809831

给dialog确定取消点击事件

image-20221101174839511

点击确定给参数放进去,并且sync修饰符的自定义事件传值关闭弹出层

image-20221101175026985

点击取消直接关闭弹出层

如此就完成了给用户分配角色,接下来就是给角色分配权限

三.权限点管理页面

3.1 新建权限点管理页面

在企业服务中权限一般为:页面访问权限 、按钮操作权限、api访问权限(多见于在后端进行拦截),前端一般就前两个

而且我们一般是先有访问权再有按钮操作权,连页面都看不到何谈按钮操作

image-20221102125334509

一般权限管理页面都是tree树形结构,但是除了tree这个组件我们还可以用table组件来做

image-20221102130012706

结构就像这样一个添加按钮,一个表格里面四列

image-20221102130540054

创建一个接口统一管理文件,这个页面要发起五个请求,分别是获取权限列表,添加权限、查看权限、修改和删除

image-20221102132947638

3.2 获取数据转化树形

调用接口请求,赋值data,并绑定到页面渲染

image-20221102133336197

这样就渲染上去了,但是只有一级目录

image-20221102133452533

我们目前只有一级,但是我们的二级三级都是在这个列表里面的

可以看到我们的数据结构,type对应层级,二级的pid就是他的父级的id

image-20221102140552754

所以这里有一个专门的解决思路,针对于我们的list数据没有children,只有id和pid转化为树形结构的封装函数

第一个参数是我们的列表,第二个参数为我们的根植,也就是pid在一级下的一个值,找到这就说明到头了

在函数里面定义一个新数组,对我们的列表进行循环

第一次判断我们的一级都会判断成功进来,然后利用递归还是list去找,但是根值为我们的当前一级的id,也就是去查找list里面pid等于一级id的item

image-20221102135027725

所以我们可以配合这个函数直接将我们的list改变结构

image-20221102143228491

还没完,我们的table组件确实有树形结构,他说的很清楚要加一个row-key而且必须为唯一指定值

image-20221102143411047

image-20221102143451159

现在就有结构了

image-20221102143518384

由于我们支持两层,一个页面访问权,一个按钮权

image-20221102143618004

所以这个添加操作,只有一级才可以添加

image-20221102143741067

3.3 新增编辑权限弹层

只需要前四个

image-20221102144025706

用dialog里面放form

image-20221102144542685

创建好要收集的data

image-20221102144610268

v-model绑定上来

image-20221102144636194

注意一下这里的switch

按理说他应该是一个Boolean,但是查看文档可知,他也可以绑定字符串数字等

image-20221102144728222

只不过绑定其他值的时候我们需要设置两个属性,让他知道什么代表开什么代表关

image-20221102144758647

image-20221102144839851

底部确定与取消的固定套路写法

image-20221102144923862

3.4 新增、编辑、删除权限点

先删除

image-20221102145017441

通过confirm弹出提示框执行两个成功回调,注意returnpromise的结果,可以在后面链式编程继续then接受他的成功回调

image-20221102145349458

添加

我们有两个添加,上面是添加页面访问权,下面这个是添加按钮操作权

image-20221102151028446

给他们都来同一个点击事件回调打开刚才的dialog,通过type来判断是页面权还是按钮权,还要传进来一个id,如果是页面权直接为0,按钮权的pid就为当前row的id

image-20221102151530285

然后校验一些表单

image-20221102151610662

image-20221102151627221

当我们点击确定就应该去保存发起请求了

首先我们还要校验一下表单验证

image-20221102151728137

注意验证除了平时那种用法,还可以用回调的用法

校验成功发起请求,请求成功调用message提示

image-20221102151951498

编辑

获取当前这一行的数据,进行回显

image-20221102152242368

但是点击确定要修改一下,要判断是编辑还是新增

直接加一个if判断有id就是编辑,没有id就是新增

image-20221102152329497

四.给角色分配权限

4.1 分配权限弹出层

image-20221102161437617

同样是dialog,先完成下面确定取消按钮

image-20221102161838342

上面为一个树形结构

image-20221102162404123

给分配权限来一个点击事件,里面还是通过pid和id转为tree结构的列表函数来发起请求

image-20221102162712229

但会发现有行数没有字体

image-20221102162557826

这个时候就要用到我们的tree组件第二个绑定的值,它是用来定义显示字段的名称

image-20221102162810158

将里面的每一项添加勾选框

image-20221102163012567

以及当我点击他的父级,子级不会默认勾选上,因为我如果只想让他有页面权,没有按钮权,并不需要勾选父级,子级也能勾选上

image-20221102163133366

image-20221102163146307

点击分配权限将id保存下来,不管是看你已经有的权限点,还是点击确定保存新增删除的,都需要他

image-20221102163358229

获取当前用户所拥有的权限点

image-20221102163651392

但是你会发现返回的是一个数组并且全是这个权限的id

这个时候tree组件又有两个属性

node-key绑定这个树形的唯一标识

image-20221102163747866

默认勾选的节点数组

image-20221102163847825

然后给他赋值,并且作为default的值即可

image-20221102163923108

image-20221102163933805

4.2 给角色分配权限

就是当我们点击确定按钮应该去发起请求

首先要收集我们勾选上的值

在tree有一个方法,返回勾选的节点形成的数组,又因为我们前面设置了唯一标识为id,所以这个数组就是id形成的

image-20221102165310428

我们的接口刚好需要权限点的id和当前这个角色的id

怎么来调用这个方法

可以通过给tree来一个ref来调用

image-20221102165438980

这个时候可以完成权限分配了,但是还有个小bug当我们点击确定后再次点击会发现还是显示的之气默认勾选上的,那是因为我们显示勾选上的这个数组还没有清空,并且我们清空是在取消这个函数里面清空

image-20221102165824214

首先要知道这个取消函数不光是绑定給这个取消按钮的,还有我们的dialog close事件,也就是右上角的x

image-20221102165920517

所以我们点击确定保存之后会将show改为false,相当于执行了一下close事件

自此给员工分角色,给角色分配权限就已经完成,rbac的权限数据层完成

五.前端权限应用-页面访问和菜单

5.1 主体思路

在我们之前返回的权限点的数据中可以看到除了name、id、pid之外还有一个属性 标识

这个标识可以跟我们路由模块相关联,意思就是该用户有这个标识就能访问这个路由,没有就不能访问

image-20221102170942268

用到vue-router提供的一个方法 addRoutes

大体思路如下

image-20221102170923213

5.2 新建vuex管理权限模块

在vuex新建一个js模块

image-20221102171316842

在这里面先完成一个逻辑,导入我们的常量路由,也就是每个人都拥有的路由比如404、登录、首页等等

直接让我们的routes等于常量路由

然后我们要对这个routes做一些操作,也就是让常量路由➕你自己拥有权限的路由就等于真正的你能访问到的路由

image-20221102172055117

但是这里这么写会有问题,我们如果用state.routes作为每次的基础值,那么如果前面是管理员登录,他拥有100个页面的权限,这个时候已经给到了routes,我后面又用另一个人来登录,他就会有管理员的基础页面权限,所以是不对的

image-20221102172321047

应该每次用常量路由来进行一个比较

image-20221102172349409

5.3 vuex筛选权限路由

之前做登录的路由守卫做过这样一个验证

image-20221102175021739

当我们有token,页面不在登录页要获取用户资料之后,这个藏着改用户所有的权限标识就在这里面

所以我们应该在这里来完成

image-20221102175402184

发现没有用户信息,就去获取

image-20221102175457860

image-20221102175833073

在这个actions里面就能拿到我们的标识,怎么和路由进行比对,我们每个路由都设置的有name,和name比较即可

image-20221102175636260

然后我们需要在刚才的vuex新增的模块来一个actions筛选权限路由

我们这里异步路由是这样设置的,因为分了模块化

image-20221102175919562

image-20221102175927514

在这个筛选函数里面第一个参数context,第二个参数就是传进来的menus,直接对他来一个遍历,每一个item就是标识

image-20221102180022614

导入异步路由,在遍历里面让他所拥有的的每一个权限的标识去和异步路由name做一个filter,这个方法会返回一个筛选完的数组

我们数组不能push数组,所以可以先给他解开

image-20221102180156639

然后将筛选出的路由给到mutations,也就是会赋值给state

image-20221102180306127

这里commit是为了给到state也就是为了左侧的菜单的一个显示,而return是为了后面addRoutes这个方法,它是为了我们的url的一个路由显示

5.4 权限拦截出调用筛选权限action

现在回到我们刚才所说的应该在路由守卫的那个位置来dispatch我们的筛选路由

image-20221102180929724

还没完,我们还要发参数,在我们上面获取用户信息的函数,专门return了一个返回值

image-20221102180846730

拿到返回值,传参数

这里为什么后面这个也要接收返回值

image-20221102181229637

别忘了,我们的这个actions也定义了一个routes的return

image-20221102181446862

这里有一个注意点,如果这里调用addRoutes那就必须用next(to.path)不能直接用next()

这是vue-router的一个bug

image-20221102181930755

5.5 常量和异步理由解除合并

image-20221102182204204

image-20221102182350602

现在url访问自己有权限的能访问

image-20221102182429905

没有权限的为404

image-20221102182449870

image-20221102182459986

但是现在左侧菜单栏还没有显示出来

原因是因为这个模板左侧菜单遍历的是路由表中的路由

image-20221102182633718

因为我们的addroutes并不是动态变化的,我们说的commit是给左侧菜单用的,这时候就发挥用场了

创建一个getters

image-20221102182801680

直接导入

并且将我们之前的删除

image-20221102182900283

六.登出重置路由权限和404问题

首先是我们点击退出登录还会有问题,就是这个时候可以看到我们的state里面的routes还为上一个用户的权限路由,并且我在url去输入对应的路由还可以进入

image-20221102195613205

这是因为我们一直在addRoutes而没有在登出去重置或者删除他

在我们的vuerouter文件里面可以看到一个函数,这个是重置路由的函数

image-20221102195800761

那么我们应该在登出的vuex里面调用一下这个方法

image-20221102195854541

image-20221102195900458

然后第二部操作应该将state里面的routes为初始状态

我们应该调用这个mutations,然后传进去的参数为空数组

image-20221102200016530

问题来了,我们这里是在一个vuex里面要去commit兄弟级别的vuex里面这个怎么来做

image-20221102200058221

子模块调用子模块,两种情况,一种是都没加锁的情况都没有命名空间的情况,那就可以直接调用,因为都默认是在全局配置下的

第二种是都加了命名空间的情况,我们的commit包括dispatch其实是有第三个参数的,第三个参数是一个对象,如果来一个root:true,表示调用根级的子模块

image-20221102200322992

解决第二个问题

当我们一刷新就会出现404

看到我们的router配置文件,有一个404的重定向路由,他有一个声明说的必须放到最后,这里我们由于把她放进了常量路由,又加了一些vuex里面的路由再加上异步路由导致他虽然这里是在最后,但其实已经没有在最后的位置了

image-20221102200602126

所以我们应该把这一段删除,然后添加在我们addRoutes这里

image-20221102200708173

七.功能权限应用

7.1 功能权限的受控思路

前面我们完成了页面的访问权,但是该页面中某些功能用户可能有也可能没有,这就是功能受限

也是在我们userInfo数据里面的points这个属性里面

image-20221102204141195

就是我们权限管理下页面权限下

image-20221102204219035

这就表示有新增的按钮权限

image-20221102204342896

image-20221102204431118

就是让有这个权限的按钮显示,没有就隐藏

7.2 使用mixin技术将检查方法注入

定义规则

image-20221102205041482

全局混入

image-20221102205119868

然后那个按钮有权限就调用这个方法,参数为这个按钮的权限标识

image-20221102205426591

image-20221102205540450

当然你直接是想连看都看不到

image-20221102205600013

posted @ 2022-12-28 12:13  Heymar-10  阅读(2036)  评论(2编辑  收藏  举报