浅谈管理系统中关于菜单、路由、按钮权限的实现过程
世界管理系统千千万,每个系统都可能有不同的权限管理方式,代码变化万千,但万变不离其宗
一、什么是权限
- 现代系统肯定会有这些操作:
- 登录:要知道,计算机是不知道你是谁的,而登录就是为了记录你的状态信息,只有在成功后,才会给用户颁发一个独立的签名--token,它记录着用户的登录信息、登录时间等等。当拿到这个签名后,只有携带这张身份证才可获得想要访问的数据内容。
- 跳转:登录成功后不可能一直给用户展示登录页面吧?跳转页面是接下来的最重要的方式,况且用户的目的就是为了访问系统里面的内容
- 展示:需要向用户展示什么内容?需要为用户提供什么功能?需要用户怎么操作?这些都是有关权限的东西
- 要知道,管理系统并非每个人使用都一样的,某些敏感保密的数据是不会让所有人看到的,每个人/岗位就应该做自己该做的事。
- 并且繁杂的大型管理系统,可能对于某些使用人员只需关注自己所在意的功能即可
- 如何限制这些问题,就需要权限来管理,在系统层面/代码层面就进行限制
二、现代关于权限的痛点
- 对我来说,我接触过很多管理系统的研发、维护工作,总结几点就是
- 有的对权限一点都不理解,完全没经过设计就写
- 有的系统权限写的一塌糊涂,全由前端代码来限制
- 有的代码写的一塌糊涂,这一点那一点完全不能看,不给人维护的机会
三、设计
- 进入主题,开始上才艺
- 管理系统中权限系统搭建可以简单分为三个部分
- 菜单:控制所有的路由、菜单、按钮、权限值等记录信息
- 角色:每个角色都有每个角色对应的菜单系统,可以划分不同角色配置不同的菜单/按钮等权限
- 用户:每个用户挂载不同的角色,根据角色加载有对应权限的数据
3.1、动态路由
- 以vue3为例,通过vue-router@4.x 的
addRoute,可以很方便的实现这个需求 - 菜单管理记录着每个菜单的路由信息,在用户登录后,根据角色可获取到对应角色的菜单数据
- 前端开发人员拿到数据后,根据每个对象去遍历查找,动态添加路由
// 路由信息,可根据webpack/vite 动态导入,最后获得的结果
const home = {
path: '/home‘,
name: 'Home',
component: () => import('@/views/Home.vue')
}
const about = {
path: '/about‘,
name: 'About',
component: () => import('@/views/About.vue')
}
export default [home, about]
// 菜单数据
import dynamicRoutes from './dynamic-routes.js'
import router from '@/router';
const menus = [{ route: '/home', ...}, { route: '/about', ...}]
// 前端记录的每个路由信息 注:没有添加到初始化routes中
// 设计函数,根据菜单数据,映射动态路由
const patchRoutes = (menus) => {
menus.forEach(menu => {
const resultRoute = dynamicRoutes.find(item => item.path === menu.route)
if (resultRoute) {
router.addRoute(resultRoute)
}
})
}
// 加载菜单数据接口的方法调用
patchRoutes(menus)
- 上面是一个简单的根据菜单注册动态路由的过程,可根据具体业务需求在做操作
3.2、按钮权限
-
如何判断按钮是否展示,这一步在前面的菜单管理已经设计好了,菜单管理记录着每个路由下的按钮信息(只做记录),包括按钮权限值
-
当在角色数据中添加该菜单下的按钮后,即可完整的获取该角色对有权限的按钮数据的权限值
- 通常前端只需要定义权限值,推荐 模块名:按钮操作,例如:
menu:delete - 前端需要在代码层面根据这个权限值判断是否展示这个按钮
- 后端需要提供根据角色查询出来的权限值所有数据
- 通常前端只需要定义权限值,推荐 模块名:按钮操作,例如:
-
在vue3中,可以根据指令,来完成这个按钮的显隐操作
<template>
<div class="test-permission">
<button v-permission="'home:delete'">删除按钮</button>
</div>
</template>
<script setup lang="ts">
// 这块拿到permission 数据
const permissions = ['home:delete', 'about:delete', ....]
const vPermission = {
mounted(el: HTMLElement, binding: any, vnode: VNode) {
if (!permissions.includes(binding.value) {
// 说明没有权限,移除节点
el.parentElement?.removeChild(el)
}
}
}
</script>
3.3、优缺点
-
优点:
- 逻辑清晰,可维护性强
- 可以完全实现前端路由状态具体化,限制不可访问的人员进行访问
- 可根据需求写一个自动生成工具(如果感兴趣,后续可能会写相关文章)
-
缺点:
- 需要编写的文件/定义的变量比较繁琐
- 权限值如有规律的话,可能会被个别有心人员破解,所以需要后端开发人员在后端根据按钮权限值对接口权限进行控制
- 按钮权限值一经定义,不可修改,这点是个设计缺陷,但目前来说,限制角色修改权限,可以解决这个问题
四、总结
- 权限管理设计是作为一个开发人员必经之路,如何设计,如何巧妙地用最少得缺陷解决最大的问题,这个需要好好反思
- 代码质量一定要过关!!!毕竟谁都不想维护看不懂的东西
- 文章可能写的有点潦草,可以借鉴,代码验证过没问题,有问题可以一起讨论