一.概述
传统权限管理:
类似于这样,每新增一个人都要重新给她一些权限,是针对每个人单独设置的,这种方法已经不适用于高效管控权限的
基于此,RBAC权限模型就诞生了,Role-Based Access control也就是基于角色的权限控制,相对于传统模式,这套方案强调一个role角色
RBAC实现了用户和权限点的分离,相对某个用户设置权限,不用再去一个权限一个权限的给他单独设置,而是直接给他设置角色即可,这样权限的分配和设计就达到了极简,高效,当想对用户收回权限时,只需要收回角色即可
二.分配员工角色
2.1 新建角色窗体
应该在点击角色时弹出角色窗体
首先新建一个弹出层dialog的组件
里面标签体dialog的一个居中布局
注意el-row快速变为flex布局,且居中分布
然后我们中间的内容部分用到组件 ,复选框组checkbox-group,中间是我们后面的角色循环每一项
注意这个组件接受两个父组件传过来的值,一个是该dialog显示隐藏的参数,一个是当前点的这个用户的id等信息,我拿到了才能去给这个用户添加角色
2.2 获取角色列表和当前用户角色
首先需要在父组件导入弹出组件并应用在页面中
然后我们首先需要传控制当前弹出显示隐藏的数据
在父组件data定义好一个变量
给子组件传过来
注意我们props是多个单词采用驼峰命名法,也就是props是支持驼峰命名的,但是在用的时候,也就是传过来的值这里要采用首字母全小写,连接线连接的形式
后面加一个.sync的修饰符瞬间形成了自定义事件,方便我们后面子组件点击取消确定等修改他的值
然后我们还要定义一个userId给子组件传过来
给角色按钮添加点击事件,传参当前这一行的id
在这个事件立面展示dialog,将userid赋值即可
然后就应该在子组件这边来操作了
之前在做这个页面的时候,获取过角色列表
按需导入我们这个请求函数
然后再created钩子发起请求
这里注意,我们这个接口默认是返回十条数据,我们就传进去参数20,就假装默认为有20个角色,一般角色也不会超过很多
然后取里面的角色列表给到data
遍历循环我们的复选框每一项
注意这里面的label,我们element组件说的很清楚,这里label既是显示的值,也充当我们收集的值
但是我这里只想收集id,显示name怎么办
他存他的,我显示的内容用一个插值语法来代替即可
然后我们得checkbox-group也要绑定一个数组用来存储我们选择的一些id值
data先定义
然后我们需要将当前用户已经绑定好的一些角色权限一打开就勾选上的状态
这里是这样做的
还是在子组件导入请求函数,并写在方法内
但是我们这里不是在这里调用,是我们父组件一点击角色这个事件里面来调用
这里不能直接用父组件传过来的id作为参数
因为我们这个id是props接受的,一个重要概念,props赋值是异步的,所以我们要一点击角色打开的瞬间就发起请求,这个时候没有这个id
我们可以通过给外层函数接受一个参数,然后我们的接口函数拿这个参数作为id,要知道我们是在父组件来调用
给子组件打标识拿到实例,然后在点击事件调用这个函数
我们每次点开会有一个闪空白的瞬间
解决思路:
其原因是因为我们定义的这个函数因为async和await所以它是一个异步函数,而我们点击事件里面,展示为true是同步操作
所以我们应该让方法调用完执行完了再去展示
那么就可以给我们的这个调取实例的函数调用来一个await 等待他执行完才去执行下面的
2.3 给员工分配角色
定义好保存用户角色的接口函数
给dialog确定取消点击事件
点击确定给参数放进去,并且sync修饰符的自定义事件传值关闭弹出层
点击取消直接关闭弹出层
如此就完成了给用户分配角色,接下来就是给角色分配权限
三.权限点管理页面
3.1 新建权限点管理页面
在企业服务中权限一般为:页面访问权限 、按钮操作权限、api访问权限(多见于在后端进行拦截),前端一般就前两个
而且我们一般是先有访问权再有按钮操作权,连页面都看不到何谈按钮操作
一般权限管理页面都是tree树形结构,但是除了tree这个组件我们还可以用table组件来做
结构就像这样一个添加按钮,一个表格里面四列
创建一个接口统一管理文件,这个页面要发起五个请求,分别是获取权限列表,添加权限、查看权限、修改和删除
3.2 获取数据转化树形
调用接口请求,赋值data,并绑定到页面渲染
这样就渲染上去了,但是只有一级目录
我们目前只有一级,但是我们的二级三级都是在这个列表里面的
可以看到我们的数据结构,type对应层级,二级的pid就是他的父级的id
所以这里有一个专门的解决思路,针对于我们的list数据没有children,只有id和pid转化为树形结构的封装函数
第一个参数是我们的列表,第二个参数为我们的根植,也就是pid在一级下的一个值,找到这就说明到头了
在函数里面定义一个新数组,对我们的列表进行循环
第一次判断我们的一级都会判断成功进来,然后利用递归还是list去找,但是根值为我们的当前一级的id,也就是去查找list里面pid等于一级id的item
所以我们可以配合这个函数直接将我们的list改变结构
还没完,我们的table组件确实有树形结构,他说的很清楚要加一个row-key而且必须为唯一指定值
现在就有结构了
由于我们支持两层,一个页面访问权,一个按钮权
所以这个添加操作,只有一级才可以添加
3.3 新增编辑权限弹层
只需要前四个
用dialog里面放form
创建好要收集的data
v-model绑定上来
注意一下这里的switch
按理说他应该是一个Boolean,但是查看文档可知,他也可以绑定字符串数字等
只不过绑定其他值的时候我们需要设置两个属性,让他知道什么代表开什么代表关
底部确定与取消的固定套路写法
3.4 新增、编辑、删除权限点
先删除
通过confirm弹出提示框执行两个成功回调,注意returnpromise的结果,可以在后面链式编程继续then接受他的成功回调
添加
我们有两个添加,上面是添加页面访问权,下面这个是添加按钮操作权
给他们都来同一个点击事件回调打开刚才的dialog,通过type来判断是页面权还是按钮权,还要传进来一个id,如果是页面权直接为0,按钮权的pid就为当前row的id
然后校验一些表单
当我们点击确定就应该去保存发起请求了
首先我们还要校验一下表单验证
注意验证除了平时那种用法,还可以用回调的用法
校验成功发起请求,请求成功调用message提示
编辑
获取当前这一行的数据,进行回显
但是点击确定要修改一下,要判断是编辑还是新增
直接加一个if判断有id就是编辑,没有id就是新增
四.给角色分配权限
4.1 分配权限弹出层
同样是dialog,先完成下面确定取消按钮
上面为一个树形结构
给分配权限来一个点击事件,里面还是通过pid和id转为tree结构的列表函数来发起请求
但会发现有行数没有字体
这个时候就要用到我们的tree组件第二个绑定的值,它是用来定义显示字段的名称
将里面的每一项添加勾选框
以及当我点击他的父级,子级不会默认勾选上,因为我如果只想让他有页面权,没有按钮权,并不需要勾选父级,子级也能勾选上
点击分配权限将id保存下来,不管是看你已经有的权限点,还是点击确定保存新增删除的,都需要他
获取当前用户所拥有的权限点
但是你会发现返回的是一个数组并且全是这个权限的id
这个时候tree组件又有两个属性
node-key绑定这个树形的唯一标识
默认勾选的节点数组
然后给他赋值,并且作为default的值即可
4.2 给角色分配权限
就是当我们点击确定按钮应该去发起请求
首先要收集我们勾选上的值
在tree有一个方法,返回勾选的节点形成的数组,又因为我们前面设置了唯一标识为id,所以这个数组就是id形成的
我们的接口刚好需要权限点的id和当前这个角色的id
怎么来调用这个方法
可以通过给tree来一个ref来调用
这个时候可以完成权限分配了,但是还有个小bug当我们点击确定后再次点击会发现还是显示的之气默认勾选上的,那是因为我们显示勾选上的这个数组还没有清空,并且我们清空是在取消这个函数里面清空
首先要知道这个取消函数不光是绑定給这个取消按钮的,还有我们的dialog close事件,也就是右上角的x
所以我们点击确定保存之后会将show改为false,相当于执行了一下close事件
自此给员工分角色,给角色分配权限就已经完成,rbac的权限数据层完成
五.前端权限应用-页面访问和菜单
5.1 主体思路
在我们之前返回的权限点的数据中可以看到除了name、id、pid之外还有一个属性 标识
这个标识可以跟我们路由模块相关联,意思就是该用户有这个标识就能访问这个路由,没有就不能访问
用到vue-router提供的一个方法 addRoutes
大体思路如下
5.2 新建vuex管理权限模块
在vuex新建一个js模块
在这里面先完成一个逻辑,导入我们的常量路由,也就是每个人都拥有的路由比如404、登录、首页等等
直接让我们的routes等于常量路由
然后我们要对这个routes做一些操作,也就是让常量路由➕你自己拥有权限的路由就等于真正的你能访问到的路由
但是这里这么写会有问题,我们如果用state.routes作为每次的基础值,那么如果前面是管理员登录,他拥有100个页面的权限,这个时候已经给到了routes,我后面又用另一个人来登录,他就会有管理员的基础页面权限,所以是不对的
应该每次用常量路由来进行一个比较
5.3 vuex筛选权限路由
之前做登录的路由守卫做过这样一个验证
当我们有token,页面不在登录页要获取用户资料之后,这个藏着改用户所有的权限标识就在这里面
所以我们应该在这里来完成
发现没有用户信息,就去获取
在这个actions里面就能拿到我们的标识,怎么和路由进行比对,我们每个路由都设置的有name,和name比较即可
然后我们需要在刚才的vuex新增的模块来一个actions筛选权限路由
我们这里异步路由是这样设置的,因为分了模块化
在这个筛选函数里面第一个参数context,第二个参数就是传进来的menus,直接对他来一个遍历,每一个item就是标识
导入异步路由,在遍历里面让他所拥有的的每一个权限的标识去和异步路由name做一个filter,这个方法会返回一个筛选完的数组
我们数组不能push数组,所以可以先给他解开
然后将筛选出的路由给到mutations,也就是会赋值给state
这里commit是为了给到state也就是为了左侧的菜单的一个显示,而return是为了后面addRoutes这个方法,它是为了我们的url的一个路由显示
5.4 权限拦截出调用筛选权限action
现在回到我们刚才所说的应该在路由守卫的那个位置来dispatch我们的筛选路由
还没完,我们还要发参数,在我们上面获取用户信息的函数,专门return了一个返回值
拿到返回值,传参数
这里为什么后面这个也要接收返回值
别忘了,我们的这个actions也定义了一个routes的return
这里有一个注意点,如果这里调用addRoutes那就必须用next(to.path)不能直接用next()
这是vue-router的一个bug
5.5 常量和异步理由解除合并
现在url访问自己有权限的能访问
没有权限的为404
但是现在左侧菜单栏还没有显示出来
原因是因为这个模板左侧菜单遍历的是路由表中的路由
因为我们的addroutes并不是动态变化的,我们说的commit是给左侧菜单用的,这时候就发挥用场了
创建一个getters
直接导入
并且将我们之前的删除
六.登出重置路由权限和404问题
首先是我们点击退出登录还会有问题,就是这个时候可以看到我们的state里面的routes还为上一个用户的权限路由,并且我在url去输入对应的路由还可以进入
这是因为我们一直在addRoutes而没有在登出去重置或者删除他
在我们的vuerouter文件里面可以看到一个函数,这个是重置路由的函数
那么我们应该在登出的vuex里面调用一下这个方法
然后第二部操作应该将state里面的routes为初始状态
我们应该调用这个mutations,然后传进去的参数为空数组
问题来了,我们这里是在一个vuex里面要去commit兄弟级别的vuex里面这个怎么来做
子模块调用子模块,两种情况,一种是都没加锁的情况都没有命名空间的情况,那就可以直接调用,因为都默认是在全局配置下的
第二种是都加了命名空间的情况,我们的commit包括dispatch其实是有第三个参数的,第三个参数是一个对象,如果来一个root:true,表示调用根级的子模块
解决第二个问题
当我们一刷新就会出现404
看到我们的router配置文件,有一个404的重定向路由,他有一个声明说的必须放到最后,这里我们由于把她放进了常量路由,又加了一些vuex里面的路由再加上异步路由导致他虽然这里是在最后,但其实已经没有在最后的位置了
所以我们应该把这一段删除,然后添加在我们addRoutes这里
七.功能权限应用
7.1 功能权限的受控思路
前面我们完成了页面的访问权,但是该页面中某些功能用户可能有也可能没有,这就是功能受限
也是在我们userInfo数据里面的points这个属性里面
就是我们权限管理下页面权限下
这就表示有新增的按钮权限
就是让有这个权限的按钮显示,没有就隐藏
7.2 使用mixin技术将检查方法注入
定义规则
全局混入
然后那个按钮有权限就调用这个方法,参数为这个按钮的权限标识
当然你直接是想连看都看不到