浅谈vue后台管理系统权限控制思考与实践


Posted in Javascript onDecember 19, 2018

前言

最近在开发管理系统时遇到了任何管理系统都会有的需求---权限控制,之前也遇到过这种需求,但是架构不完善导致的各种问题使得后期维护非常麻烦,这一次的方案解决了之前的种种问题,现做一次记录,当然这个架构后期可能会有坑,不过得一步一步的尝试才能发现并解决问题。

权限控制需求

因为是单页面应用,路由交给前端来控制,对于一些需要特定权限才能查看的信息的保护变得尤为重要,如果前端不做好权限校验,后端也一时疏忽,就可能就会导致数据泄露。

对于权限控制,需求大致为如下:

  1. 对于大模块的限制,比如需要通过路由跳转的模块,这时需要进行路由拦截
  2. 对于小功能的限制,比如一个按钮,如果没有特定权限,那么这个按钮就不显示

安全层面的思考

之前接手了一个管理系统,前端是将权限列表存储在storage中来实现长久储存,这种实现方式是很不可取的,因为hacker可以通过手动更改存储的信息来实现获取特定权限,甚至系统都没有做路由拦截,如果知道模块的路由,可以直接通过输入路由信息来直接跳转到特定模块。对于一些模块的权限,权限被管理员修改后也无法立即生效,所以对于这几种情况做了如下思考与实践。

权限被管理员修改后立即生效

对于这个需求,我的做法是,获取到权限列表后,将权限信息存储在 vuex store 中,并且使用getter函数,对于是否可以使用该权限进行判断,这样一旦权限数据更新,前端权限限制功能点会自动修改,从而做到权限的实时性,大致实现如下:

// vuex state.js
export default {
  userPrivileges: {
    admin: [],
    purchaser: []
  }, // 用户权限信息
}
// vuex getters.js
export default {
  canIUse: state => (role, id) => state.userPrivileges[role].includes(id)
}

// 页面具体小功能,通过 mapGetters 引入 canIUse 函数
<span v-if="canIUse('admin', 9)">{{scope.row.allocation_subtotal}}</span>

这样一来,数据存储在内存中,那么权限信息就无法轻易的被修改,同时对于权限的判断也非常简单,只需要在特定功能点传入功能点的权限id就能判断是否可以使用这个权限了。

但是将数据存储在了内存中也会遇到一个问题,页面刷新怎么办?接下来就是讲解这种情况。

刷新页面也可以进行权限判断

对于大模块的权限拦截,肯定是通过路由钩子来进行拦截的(这种实现有很多文章讲解过,这里不具体讲解),但是通过路由钩子进行拦截的前提是,权限信息得提前存在。

刷新页面会存在这种情况,页面刷新时,先执行的路由钩子,再执行的组件生命周期钩子来请求权限的列表,此时权限信息不存在,那么页面跳转到登陆页的话,体验就不够友好。

所以我的做法是,建立一个中间页,如果权限校验不通过,那么跳转至中间页,中间页进行权限的请求,请求到权限后,再判断是否可以跳转,这样的话,刷新页面体验就比较好。大致代码如下:

// vuex actions.js
// 通过返回一个promise,使得store更新与后续代码变为“同步”执行
export default {
  getUserPrivileges({ commit }) {
    return fetch.get({
      url: '/currentstaff'
    }).then(data => {
      commit('SET_USER_PRIVILEGES_INFO', data.data)
      return data.data
    }).catch(e => {

    })
  }
}
// router.js
// 需要验证权限的路由
{
  path: 'suppliers',
  component: Suppliers,
  meta: {
    role: 'admin',
    privilegeId: 5
  }
}

function isCanUseThisModule(to, from) {
  return to.matched.every(record => {
    // 利用路由meta存储相应权限信息
    if (record.meta.role) {
      return store.getters.canIUse(record.meta.role, record.meta.privilegeId)
    } else {
      return true // 如果不需要权限,直接返回true
    }
  })
}

router.beforeEach((to, from, next) => {
  if (isCanUseThisModule(to, from)) {
    next() // 权限验证通过,跳转下一路由
  } else {
    next({
      path: '/main/privilegeValidator' // 权限验证不通过时的中间页
    })
  }
})

// 权限校验中间页代码示例
created() {
  this.$store.dispatch('getUserPrivileges').then(data => {
    for (let i = 0; i < data.admin_permissions.length; i++) {
      if (this.canIUse('admin', data.admin_permissions[i])) {
        this.$router.push({
          path: this.routerList.find(value => value.privilegeId === data.admin_permissions[i]).linkHref
        })
        return
      }
    }
    this.$router.push('/login') // 如果没有任何权限,则跳转登陆页面
  })
}

用户在登陆后也可以跳转到这个权限中间页,进行权限判断后再跳转到对应模块。

尾声

大致的实现过程就是这样,希望对大家有所帮助,如果有暗坑还请指出。也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
用js判断用户浏览器是否是XP SP2的IE6
Mar 08 Javascript
jQuery 表单验证扩展代码(二)
Oct 20 Javascript
JQuery实现当鼠标停留在某区域3秒后自动执行
Sep 09 Javascript
原生JavaScript实现动态省市县三级联动下拉框菜单实例代码
Feb 03 Javascript
原生js三级联动的简单实现代码
Jun 07 Javascript
解析jQueryEasyUI的使用
Nov 22 Javascript
vue 设置路由的登录权限的方法
Jul 03 Javascript
jQuery实现带3D切割效果的轮播图功能示例【附源码下载】
Apr 04 jQuery
VUE+elementui面包屑实现动态路由详解
Nov 04 Javascript
如何基于js判断浏览器版本
Feb 20 Javascript
jQuery实现简单飞机大战
Jul 05 jQuery
JS实现拖拽元素时与另一元素碰撞检测
Aug 27 Javascript
如何为vue的项目添加单元测试
Dec 19 #Javascript
浅谈Angular7 项目开发总结
Dec 19 #Javascript
mockjs+vue页面直接展示数据的方法
Dec 19 #Javascript
vue项目搭建以及全家桶的使用详细教程(小结)
Dec 19 #Javascript
vue使用Google地图的实现示例代码
Dec 19 #Javascript
JS实现获取自定义属性data值的方法示例
Dec 19 #Javascript
vue动态绑定class选中当前列表变色的方法示例
Dec 19 #Javascript
You might like
基于flush()不能按顺序输出时的解决办法
2013/06/29 PHP
Smarty环境配置与使用入门教程
2016/05/11 PHP
[原创]php使用strpos判断字符串中数字类型子字符串出错的解决方法
2017/04/01 PHP
Jquey拖拽控件Draggable使用方法(asp.net环境)
2010/09/28 Javascript
jquery写个checkbox——类似邮箱全选功能
2013/03/19 Javascript
JQuery EasyUI 加载两次url的原因分析及解决方案
2014/08/18 Javascript
javascript 面向对象封装与继承
2014/11/27 Javascript
Javascript基础教程之if条件语句
2015/01/18 Javascript
yui3的AOP(面向切面编程)和OOP(面向对象编程)
2015/05/01 Javascript
浅析AngularJS中的生命周期和延迟处理
2015/06/18 Javascript
Javascript中浏览器窗口的基本操作总结
2016/08/18 Javascript
Vue.js每天必学之构造器与生命周期
2016/09/05 Javascript
JavaScript仿支付宝6位数字密码输入框
2016/12/29 Javascript
利用Javascript实现一套自定义事件机制
2017/12/14 Javascript
JavaScript使用递归和循环实现阶乘的实例代码
2018/08/28 Javascript
微信小程序之裁剪图片成圆形的实现代码
2018/10/11 Javascript
ES6知识点整理之模块化的应用详解
2019/04/15 Javascript
layui实现三级联动效果
2019/07/26 Javascript
vue+vant实现购物车全选和反选功能
2020/11/17 Vue.js
[01:03:33]Alliance vs TNC 2019国际邀请赛小组赛 BO2 第一场 8.16
2019/08/18 DOTA
利用python画一颗心的方法示例
2017/01/31 Python
Python学习之用pygal画世界地图实例
2017/12/07 Python
Python实现曲线拟合操作示例【基于numpy,scipy,matplotlib库】
2018/07/12 Python
python实现大量图片重命名
2020/03/23 Python
Django框架安装方法图文详解
2019/11/04 Python
python__new__内置静态方法使用解析
2020/01/07 Python
python中68个内置函数的总结与介绍
2020/02/24 Python
Python 炫技操作之合并字典的七种方法
2020/04/10 Python
python 图像判断,清晰度(明暗),彩色与黑白实例
2020/06/04 Python
Python 利用OpenCV给照片换底色的示例代码
2020/08/03 Python
Python实现快速大文件比较代码解析
2020/09/04 Python
10 套华丽的CSS3 按钮小结
2012/10/03 HTML / CSS
Ever New加拿大官网:彰显女性美
2018/10/05 全球购物
SmartBuyGlasses荷兰:购买太阳镜和眼镜
2020/03/16 全球购物
文件中有一组整数,要求排序后输出到另一个文件中
2012/01/04 面试题
JS精髓原型链继承及构造函数继承问题纠正
2022/06/16 Javascript