基于Vue实现后台系统权限控制的示例代码

用Vue/React这类双向绑定框架做后台系统再适合不过,后台系统相比普通前端项目除了数据交互更频繁以外,还有一个特别的需求就是对用户的权限控制,那么如何在一个Vue应用中实现权限控制呢?下面是我的一点经验。

权限控制是什么

在权限的世界里服务端提供的一切都是资源,资源可以由请求方法+请求地址来描述,权限是对特定资源的访问许可,所谓权限控制,也就是确保用户只能访问到被分配的资源。具体的说,前端对资源的访问通常是由界面上的按钮发起,比如删除某条数据;或由用户进入某一个页面发起,比如获取某个列表数据。这两种形式覆盖了资源请求的大部分场景,因此权限控制也可以被笼统的分成菜单权限控制和按钮权限控制。

Vue菜单权限控制

菜单是对路由的直接体现,菜单控制实际上就是路由控制。实现路由控制一个简单的方式是,在路由的before钩子里校验当前即将跳转的路由地址是否有权访问,根据校验结果决定路由是否放行,伪码:

router.beforeEach((to, from, next) => { //权限校验 let pass = valid(to); if(!pass){ return console.log('无权访问'); } next(); });

这种实现方式既简单又直观,用于简单的系统非常合适,但这么做本质上是将所有路由全部注册了,直接带来的缺点有两个:一、如果路由组件不是按需加载的话,应用将加载大量冗余代码;二、每次跳转都要遍历一次完整路由是对计算能力的浪费,对于路由总数较大的应用很不可取。

理想的实现方式是本地保存完整路由,但并不立即初始化Vue应用,待用户登录拿到权限后,用菜单权限筛选出可用路由,再用可用路由初始化Vue应用。也就是说,要将登录页独立出去做成一个单独的页面,登录后将用户数据保存在本地,再通过url跳转到Vue应用所在页面,Vue应用启动前通过本地用户数据完成路由筛选,然后初始化Vue应用,伪码如下:

//main.js let user = sessionStorage.getItem('user'); if (user) { user = JSON.parse(user); //筛选得到实际路由 let fullPath = require('fullPath.js'); let routes = filter(fullPath, user.menus); //创建路由对象 let router = new Router({routes}); //生成Vue实例 new Vue({ el: '#app', router, render: h => h(App) }); }else{ location.href = '/login/'; }

这样我们就根据用户权限生成了一套”定制”路由,这时我们还希望能直接用路由生成导航菜单,常规的路由数据可能无法满足菜单组件的需求,所以我们可以事先在路由的meta数据里维护上菜单数据,比如菜单名称菜单图标等,只要在模板中通过$router.options就可以访问到当前路由数据,如果使用element-ui的菜单组件实现,代码大致是这样的:

<el-menu router> <el-menu-item v-for="(route, index) in $router.options.routes[2].children" :route="route" :index="route.name"> <i v-html="route.icon"></i>{{route.name}} </el-menu-item> </el-menu>

当然这样只能循环出一级菜单,如果还有二级路由需要对应二级菜单的话,就得判断并循环children节点,比较简单就不放更多代码了,菜单权限控制到这里就完成了。

Vue按钮权限控制

按钮权限控制与菜单权限控制的实现思路类似,也是根据用户权限判断各个按钮的显示与否,方式无非是v-if或自定义指令,而且只要将v-if背后的权限校验逻辑抽象成方法,无论是代码量还是使用形式上都跟自定义指令几乎一样,但v-if的特点是它会响应数据变化,因此随着应用的运行会频繁触发权限校验,而权限在应用的整个生命周期内其实只需校验一次,为了避免无谓的程序执行,这里可以用自定义指令来实现,伪码:

Vue.directive('has', { bind: function (el, binding) { if(!has(binding.value)){ el.parentNode.removeChild(el); } } }); //用法: <btn v-has='get,/sources'>按钮</btn>

注意在指令bind回调里有一个has()方法,这就是权限校验方法,我们同时将这个方法全局混合到Vue对象中,使应用里的每个组件都可以访问到这个方法,便于为界面上的v-if提供支持,例如:

<div v-if="has('get,/sources') && something"> 一个需要同时具备'get,/sources'权限和somthing为真值才显示的div </div>

这样一来凡是需要依据权限实现的按钮显隐控制和界面变化都可以很方便的实现。

但按钮权限控制真正麻烦的地方不在于如何实现,而在于高昂的维护成本。我们假设按钮Btn绑定了点击回调Fn,回调Fn里发起了请求Req,请求Req需要某个资源的访问权限,最终你要根据用户是否拥有Req的权限,决定Btn是否显示,而Req跟Btn之间并没有直接关联,所以我们就要人肉维护他们的关系,一个复杂项目里的按钮有个几十上百都很正常,随着业务的变更去维护这么多按钮的权限,想想都头疼。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wygxfg.html