谈谈如何设计一个友好的权限管理模块(上)

权限管理是很多管理系统常见的需求,说起来这是一个比较简单的功能,但是每个人在实现的时候,往往都是以满足自己的眼前需求为动机,不太考虑整体的设计,导致使用不友好,拓展不方便,甚至代码层面也是千奇百怪,使用各种方式的都有。

遥想几年前,我还是实习生的时候,实现自己的第一个权限方面的需求,采用的是将用户的角色存储在localStorage里面,然后前端代码根据这个角色来判断隐藏菜单栏和一些按钮。后端压根没有权限表,只有角色表。

这样的做法现在来看是很不好的,最关键的是相当于将权限写死在前端了。后来,我做其他项目的时候,曾经采用前后端交互,然后前端在最开始的时候调用权限接口,然后前端再做了一大堆处理接口返回数组的逻辑。现在来看,这样做还是有些问题,把简单的事情复杂化。

其实,权限模块可以做的完全动态化,可拓展,可编辑,使用友好。最关键的是,下次再有后端/前端兄弟和你讨论如何实现权限功能,你可以按照本文介绍的大致设计告诉他,省了很多撕逼的时间。

本文前端以Vue为例子来进行说明,后端从字段和设计表方面来说明。

首先,我们在项目里配置好路由(这一步也可以是一部分前端提前配置,一部分取后端返回的数据渲染路由),这里我们就全部由前端配置好了。

  {
        path: '/',
        name: 'layout',
        redirect: 'dashboard',
        component: () => import('@/views/layout/Layout'),
        children: [
            {
                path: 'dashboard',
                name: 'dashboard',
                component: () => import('@/views/dashboard/index')
            },
            {
                path: 'job',
                component: () => import('@/views/task/index'),
                children: [
                    {
                        path: '',
                        component: () => import('@/views/task/TaskList'),
                    },
                    {
                        path: 'create-task',
                        name: 'create-task',
                        component: () => import('@/views/task/TaskForm'),
                    }
                ]
            },
            {
                path: 'config',
                component: () => import('@/views/config/index'),
                children: [
                    {
                        path: 'task-type',
                        component: () => import('@/views/config/taskType/index'),
                    },
                    {
                        path: 'service',
                        component: () => import('@/views/config/serviceConfig/index'),
                    },
                    {
                        path: 'auto-scaling',
                        component: () => import('@/views/config/autoScalingConfig/index')
                    },
                    {
                        path: 'handler',
                        component: () => import('@/views/config/handlerConfig/index')
                    },
                    {
                        path: 'schedule',
                        component: () => import('@/views/config/detectConfig/index')
                    },
                    {
                        path: 'template',
                        component: () => import('@/views/config/templateConfig/index'),
                    },
                    {
                        path: 'context-keys',
                        component: () => import('@/views/config/contextKeys/index')
                    },
                    {
                        path: 'task-alert',
                        component: () => import('@/views/config/taskAlert/index')
                    }
                ]
            },
          
        ]
    },
    {
        path: '/others',
        component: () => import('@/views/others/index'),
        children: [
            {
                path: 'release',
                component: () => import('@/views/others/Release'),
            }
        ]
    },
    { path: '*', redirect: '/404', hidden: true },
    { path: '/404', component: () => import('@/views/error/404'), hidden: true },
    { path: '/401', component: () => import('@/views/error/401'), hidden: true },
    {
        path: '/login',
        component: () => import('@/views/login/index'),
        name: 'login',
    },

我们要明白,路由配置到底是前端配置好,还是取接口返回数据进行渲染,并不是很Matter的问题,因为我们要看重的是菜单栏和用户输入路由进入的时候的权限。

然后,我们需要有一个菜单管理的页面,在菜单管理页面,新增菜单的时候,我们把菜单分为两部分,一部分是菜单栏的目录和菜单,也就是顶部或者侧边展示的菜单栏;一部分是后端接口(包括前端的增删改查按钮,也有其他的很多接口)。这样子就完全满足任意的需求了,因为后端的接口权限也在这里直接可以编辑配置。

新增页面的字段大概如下:

后端根据菜单排序来返回数组的顺序,前端直接根据接口返回的数组渲染菜单栏。

路由地址就是前端配置好的路由里面的path的值,前端的多语言也根据这个字段作为词典即可。

是否可见,我这里的设计是因为有些没有子菜单的菜单,但是它也许会有子页面(通过$router.push方法进入),我的判断是如果它有children数组而且children的类型为菜单的可是否见均为false的话,就意味着在菜单栏里它是没有子菜单的。

功能也就是后端接口(包括前端的每个页面的增删改查):

上级层级就是我们新增出来的一个个目录:

这个树形选择框是使用的vue-treeselect。挺好的一个vue工具。

这样子,后端就使用一张菜单表,就可以满足需求了。根据选择的层级目录ID,把它放到正确的children数组里,不用再去搞乱七八糟的权限表,权限模块表等等,本来问题就是这么简单。

菜单表的list如下:

这里的Job就是没有子菜单的菜单,但是它里面是有子页面的。

然后我们新建角色表,就可以给角色分配菜单和接口的权限了:

之后我们把角色表和具体的用户关联起来即可。这里,我们既可以分配菜单,也可以分配接口,同时,页面的具体按钮可以根据是否勾选了接口来决定是否展示。没有勾选的,后端不需要返回给前端。

这样的设计即清晰明了,实现起来也简单,而且完全动态化,可编辑,对管理员来说,交互也比较方便。

至此,后端的设计基本就结束了。我们完成了菜单管理,角色管理。现在前端需要考虑如何动态的渲染菜单栏,以及在用户输入地址的时候,如何判断他是否可以进入。如果不可以进入,我们应该跳转无权限页面。

发布了49 篇原创文章 · 获赞 27 · 访问量 13万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章