22.Vue技术栈开发实战-权限控制
日常开发过程中,有不同的用户组,不同用户组看到页面上的内容是不一样的,这就涉及到权限控制。
这需要前端和后端相互配合完成。本节讲解两种权限控制的方案。
先来看下iview-admin中的代码。在路由列表配置meta为success权限字段,它是一个数组,表示当前这个界别的页面是哪个用户租可以浏览,里面的元素就代表当前的用户组,它可以是一个字符串,可以是是一个数值,都可以。如果当前的用户组是admin,而不是super_admin,那么这个页面它就看不了。在菜单里面也不会渲染,
如果这级访问不了,那么他的子集它也是访问不了的。如果这个页面任何人都可以看到,不需要设置权限的话,那么这里的 access这里删掉就可以了。
路由守卫
通过canTurnTo方法,把当前页面要访问的内容name、access用户的权限字段(是你在登陆之后,通过接口获取到的用户的权限,它是一个列表。也可以是一个数组,)参数3:routes就是整个路由列表。
传进去止之后,canTurnTo方法会通过当前用户的权限字段列表。把路由列表进行匹配。如果当前访问的页面,当前用户是有权限的,那么就可以访问,如果不能访问就跳转到401错误页面。
这样方式的好处是后端只需要返回当前用户的用户组列表。剩下的工作全交给前端,前端只需要配置权限字段access就可以了。
当用户组的数量的,达到一定级别之后,例如有上百种用户权限的话,这种方式配置起来会非常的痛苦。你可能一个页面,要在这里写几十个甚至上百个标识。所以这种情况下就适合用第二种方式
服务端返回路由列表
通过服务端返回一个可访问的路由列表,然后去做过滤,动态的把过滤出来的路由列表挂载到我们的路由实例上,
这里需要用到mock。所以这里把路由列表的注释 先去掉。使用mock进行拦截。
在mock/user.js下 加了个rules对象,一部分是page 页面级别的路由权限。里面是路由列表里面配置的所有的路由对象的name字段,
为true 就表示当前用户可以访问这个页面
false就是访问不了
component是组件级别的权限,edit_button就代表当前这个组件的标识,为true就可以显示这个组件,为false就不显示这个组件。
这种方式的弊端是在路由列表里面,所有的一级二级,包裹每一级别都需要有name
都必须要有name字段。
并且是不能重复的
不需要权限控制的页面,单独摘出来
登陆页面,我们是不需要权限的,任何用户在没有登陆的情况下都可以访问。
login和404页面
这改成routerMap
index.js内引入的时候,这样{ routes }引进来。
服务端拿到路由列表和routeMap对比,如果为true就添加进去。
我们放在store里面。添加一个router.js专门用来管理路由。
routes就是我们最终挂载到路由实例上的实例路由。
初始需要一个大家都能访问的路由,不需要权限的。
就是我们路由列表里面定义的这个
定义actions然后把这个模块导出去
把这个模块加载进来
为了演示,把之前写的持久化存储的插件先注释掉。
我们待会刷新页面的时候会有个状态表示当前有没有获取过用户权限列表。如果获取过了就不获取了。
没有获取过就调用接口获取。这里肯定是有个状态,标识有没有获取到。
如果这里你用了上面的缓存的话,它就会一直都有。而且不会去调接口。所以我们先把它持久化存储插件,先去掉。
获取用户权限列表
那么我们在哪去获取用户权限列表呢?在路由守卫这里。
把下面这里之前判断用户登陆的信息先注释掉。
写一个新的路由守卫。
首先首先还是要获取token,如果有token说明当前已经登陆。
没有登陆的情况,如果是去登陆页,那么放行,如果不是。那么就跳转到登陆页。
在store里面存一个hasGetRules。默认是false。如果你调取完接口获取了用户的权限列表,那这里就置为true
如果之类为false,就表示还没有获取,那么我们就要调用接口了。
之前在module/user模块这里会去调接口
把结果的数据返回。res.data.rules.page
调用user里面的方法,也就是上图的方法,在then里面拿到rules。拿到rules筛选过滤也是放在store里面。dispatch触发一个方法。
在/module/router.js的actions里面定义方法,首先定义一个空的路由列表routerList
如果这里全都是true,我们就不需要过滤路由列表了。
Object.entries可以把对象转换成一个数组,数组是一个二维数组,
在console里面测试这个方法,第一个值是当前的属性名,第二个值是当前的属性值。第二个元素也是一个数组,
在来个例子,这样可能看起来比较直接一些。
在这个数组上调用every方法,every方法会遍历你的数组,在里面传入回调函数
如果你没一个返回都是true的话,那么总的返回就是true,如果有一个是false。那么后面的就不去做判断了。这里直接返回false
所以我们要返回的是第二个元素,如果是true就返回true了。
如果这里整体返回true。那么就表示所有的页面你都能访问。我们就不需要通过递归去遍历所有的路由列表了。
所有过滤,所有需要判断的我们放在了这个routerMap里面
我们把这个routerMap引入,如果所有的都能访问,直接把routerMap赋值给routerList
如果不是所有的都能访问,这样我们就需要通过一个递归的方法去筛选了。
返回一个过滤。过滤我们用filter
return如果是true,那么就是要这条数据,false就不要这条数据
去rules上寻找这个item.name
我们的路由列表是一层层的嵌套的,所以就要判断有children 那么就调用自身这个方法。
拿到路由列表后,进行合并。把结果commit出去,然后方法我们在mutations里面定义。
注意合并的顺序,routerList在前,routes在后。
设置为true表示已经获取过用户的权限了。
resolve把结果返回回去。
在路由守卫里面,then里面拿到拼接后的routers.使用addRoutes发放,路由的动态的挂载上去。
有时候路由还没有挂载完 ,你直接调用next可能会有错误。这里把to通过拆分的形式,
replace设置为true表示你要访问的路径,通过替换的形式,
如果有异常直接跳转到login页面。
如果获取过路由列表直接next就可以了。
测试
登陆后报错
routes是我们挂载到store里面的。路由在跳转的时候会对路由对象做一些修改,这里的对象是引用赋值,
这里在修改的时候是修改的store里面的routes
报的错误是你应该在mutations里面修改state变量。而不是直接修改。
所有这里我们不应该传原始的routers对象,而是应该做一个深拷贝。
引入clonedeep
用clonedeep做深拷贝。
这样就不会报错了
默认的列表form是不可以访问的。
访问页面测试
这样有个坏处就是没有权限的页面,跳转的都是404页面。你实际上不知道这个页面没有,还是没有权限。
组件级别
把组件的数据存到store里面,设置rules,一开始是一个空对象。
把component传递过去。
定义mutations
在home页展示。加俩按钮
先从store里面获取这个rules
在计算属性里面
在这里先展示下拿到的rules。edit_button是显示,publish_button是不显示。
使用v-if去判断
都设置为true
都显示了
以上就是组件形式的控制
本节代码