基于Vue2.0实现后台管理系统权限控制

本文参考了基于Vue实现后台系统权限控制一文,细说自己在使用Vue2.0实现权限控制过程中的认识。

任何一个后台管理系统,都绕不开对权限控制的需求。以前ASP.NET的时代,权限控制都是通过重写Controller类中的OnActionExecuting方法,或者重写AuthorizeAttribute特性类的OnAuthorization方法等,来实现对权限的控制。

但是到了ASP.NET Core,尤其是前端采用Vue框架的以后,突然之间有点适应不过来了,特别是菜单的显示,已经不是以前后台渲染视图的实现方式了。

在我看来所谓的权限控制,其本质就是对用户发起请求的控制。用户有没有权限,其实就是能不能发起请求。只有在权限系统中,给用户分配过相应的请求权限,用户才能请求到相应的资源,否则就会被拦截。我们通常所说的权限,又可以细分为菜单以及按钮权限。菜单是对页面的请求,按钮是对资源的请求,资源可以理解为增删改查。

Vue菜单权限控制

先来说说如何实现Vue的菜单权限控制。Vue是单页面应用,通过路由来实现页面的变换。菜单权限的控制其实就是路由权限的控制。实现路由控制的一个简单方法就是在路由的before钩子里检验当前即将跳转的路由地址是否有权访问,伪码:

1
2
3
4
5
6
7
8
router.beforeEach((to, from, next) => {
//权限校验
let pass = valid(to);
if(!pass){
return console.log('无权访问');
}
next();
});

这种做法只适用于路由不多的系统,因为它有两个致命的缺点:

  1. 路由组件必须全部注册后才能使用,无法按序加载,如果遇到组件很多的情况下,应用将加载大量的冗余代码
  2. 每次跳转都要遍历一次完整的路由,浪费性能

理想的实现方式是在程序启动时,只初始化一个具有登录以及错误页面路由的Vue应用。随着用户登录拿到权限后,用菜单权限筛选出可用的路由,然后router.addRoutes()动态添加路由。如此一来就可以实现菜单权限的控制了,伪码:

1
2
3
4
5
6
7
8
9
10
11
let user = sessionStorage.getItem('user');
if(user){
user = JSON.parse(user);
//筛选得到实际的路由
let fullPath = require('fullPath.js');
let routes = filter(fullPath, user.menus);
//动态注入路由
this.extendRoutes(routes);
}else{
location.href = '/login';
}

前端使用element-ui菜单组件实现菜单渲染,伪码:

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

Vue按钮权限控制

按钮权限控制与菜单权限控制的实现思路类似,按钮的本质其实就是请求。如何控制按钮的显示与否呢?可以通过v-if或者自定义指令实现。原作者推荐使用自定义指令,理由我直接引用过来

v-if的特点是它会响应数据变化,因此随着应用的运行会频繁触发权限校验,而权限在应用的整个生命周期内其实只需要校验一次。为了避免无谓的程序执行,这里可以用自定义指令来实现

伪码如下:

1
2
3
4
5
6
7
8
9
10
Vue.directvie('has',{
bind: function(el, binding){
if(!has(binding.value)){
el.parentNode.removeChild(el);
}
}
});

//用法:
<btn v-has='get,/sources'>按钮</btn>

has()方法就是权限的校验方法,将该方法全局混合到Vue对象中,使得应用里的每个组件都可以访问到这个方法。伪码如下:

1
2
3
4
5
6
let has = function(permission){
if(!permissions[permission]){
return false;
}
return true;
}

permissions属性用来确定是否拥有资源权限,效率高于遍历原始权限数组。

另外还可以为axios请求设置拦截器,作为权限控制的第二道防线,伪码:

1
2
3
4
5
6
7
8
9
10
axios.interceptors.request.use(function (config) {
let permission = config.method + config.url.replace(config.baseURL,',');
if(!has(permission)){
//验证不通过
return Promise.reject({
message: `no permission`
});
}
return config;
});

这个拦截器很有必要,试想按钮如果还有回调请求的话,光配一个按钮权限是不够的。

总结

我说的没有原文详细,但是大致思路摆在这里,如果想要深入原理的朋友,还是欢迎去原作者的博客学习学习。

虽然这个基于Vue的权限控制方案设计得很巧妙,但是也不是完美无缺的,归根结底所有的操作都是在前端判断,在前端也就意味着,如果黑客想绕过前端权限控制的话也是有可能的,比如手动注入登录以后返回的用户权限。个人觉得还是在后端判断权限更稳妥一点,至少被修改的可能性几乎为零。但是作为拓宽思路的一种设计,应用于不是对于安全性极其严格的场景,不失为一种好的方案。

avatar

chilihotpot

You Are The JavaScript In My HTML