前后端分离下前端权限处理

10,002 阅读6分钟

权限在我看来指的是某个用户是否能够访问某个接口。对应到前端,即使某个用户访问了一个本不应该让他看到的页面,在访问接口时不具备权限,这样即使进入了页面,也不能看到相应的数据。但是我们前端要做的工作就是让用户在访问这个页面的时候,直接就收到提示,没有权限。

但现在许多项目中页面路由都是由前端去管理了,如何做到这一点呢,接下来我就分享一下在过往项目中我所用的方案。

我写了demo,上传到了github上,地址为:admin-permission-demo

数据库表是怎样设计的

一开始与后端去对接这个需求的时候,我是感到很懵逼的,觉得无从下手。因为我根本就不知道权限控制是怎么去做的,也不知道权限其实就是限制用户对于接口的访问。所以开始之前我觉得有必要说一下,权限表是怎样设计的。 我们项目中所使用的是 RBAC ,其实很简单,就是有这么三个东西:用户、角色、权限(资源)。图我就懒得画了,省事儿直接用一个别人画好的图。

权限

这个权限就是项目中的接口,意思就很明显了。给角色规定了能够访问哪些接口,然后给用户分配了角色。这样就能控制用户哪些接口能访问,哪些接口不能访问。

菜单权限与按钮权限

我们项目中权限表的大概的样子:

id p_id type permission name
主键ID 父级ID 类型:菜单或按钮 权限标识 名称

权限又分为菜单权限和按钮权限,什么意思呢?

比方说我们现在要开发一个页面,叫做用户管理,在用户管理这个页面会进行一些对用户增删查改的操作。我管这个用户管理叫做菜单权限,它下面的增删查改(接口)的操作叫做按钮权限。这些接口的p_id很显然就是这个菜单权限的主键ID了。

[
  {
    p_id: '',
    id: 1000,
    name: '权限管理',
    type: 1,   // 菜单
    permission: 'permission_manage',
    children: [
      {
        p_id: 1000,
        id: 1100,
        name: '用户管理',
        type: 1, // 菜单
        permission: 'user_manage',
        children: [
          {
            p_id: 110,
            id: 1101,
            name: '添加用户',
            type: 2, // 按钮
          }
          ...
        ]
      }
    ]
  }
]

怎么样,这样的数据结构是不是很熟悉,是不是就是管理系统中左侧菜单。这个 菜单权限,就是前端在做页面访问控制时所要用到的东西。这个数据结构只是我为了方便理解这样写的。 在我的项目中我向后端查询到的权限数据不长这个样子,就是一个数组,每一项是数据,没有做分组操作(就是没有children)。这样前端拿到数据了之后就可以根据 type 去整合成菜单权限集合和按钮权限集合,方便了后续的使用。

前端代码去实现权限控制

理解了权限是怎么设计的,接下来说说前端代码怎么去实现。

我过往的项目中所使用的都是 react + antd + dva + typescript。其实前端所选用的技术是什么并不重要,重要的是思路理解了,剩下的只是代码实现的不同。

首先我会要后端给我提供一个接口,查询当前用户的所有权限。然后我会把它分为 菜单权限按钮权限redux 去管理。

渲染左侧菜单

首先是渲染左侧菜单。在项目中一般情况下会有一个专门的左侧菜单的配置文件,在该文件里定义了菜单的名字啊、图标啊、页面路径等信息。有些菜单页面是需要被权限管理的,而有些则不需要。对于需要被权限管理的页面我会添加一个字段叫做菜单标识。

  interface MenuDataItem {
    name: string;  // 菜单名字
    icon?: string;
    path: string;
    children?: Array<MenuDataItem>;
    permission?: string;  // 菜单标识
  }
  const menuData: MenuDataItem[] = [...];

项目中的左侧菜单,就会根据这个 menuData 被渲染出来。这里的逻辑是首先看这个菜单有没有菜单标识,如果没有的话直接渲染。如果有菜单标识的话,就看用户是否具有这个菜单权限,有就渲染,没有就不渲染。每个项目的结构肯定不同,我就不多贴代码了,重要的是思路,具体实现我写了demo,传到了github上,感兴趣可以去看,这是地址:admin-permission-demo

路由层面的控制

只管理了左侧菜单的渲染肯定是不够的,需要在路由层面再去做控制。一般情况下项目中会有一份路由表,react的话路由也是组件,在路由渲染的地方去做控制就行。vue 的话是全局配置 router 的,在路由守卫中限制就行。同样的,页面对应菜单路由,看用户是否具有菜单权限,不具备就到提示权限不够页面,具备就正常显示页面。

按钮层面的控制

还是拿用户管理来举一个例子,增、删、查、改。 可能用户只具备 删、查、改的权限而不具备增的权限。这时候在页面中就不应该渲染 “添加用户” 这样的按钮了,不然用户数据输入了半天,一发请求后端一校验提示权限不足就尴尬了,我都不能做这个操作,你还展现给我干啥?

这时候去写一个组件,叫啥不重要,就比如说叫 PermissionButton 把。react的话将原本按钮作为 children 传入该组件,vue 的话应该就是 <slot>。然后该组件还接收一个 props,它是一个数组,就叫他 permissions 把。该数组的每一项就是按钮权限标识,代表需要哪些权限才能展示这个按钮,或者也有可能具有其中一项就可以展示。在提供一个props去控制把,值为 every 或者 some。every就是全都有才能展示,some就是具备其中一项就能展示。

然后就是去查用户的 按钮权限 了。 具备了改组件就展示 原本组件,不具备就不展示就行了。

总结

这是我做后台管理系统所积累的一点实践把。如果大家有何更好的方案,或者觉得哪里可以改进的欢迎交流。

对 vue 的用户可能不太友好。因为demo是react实现的。但是我并不觉得这个方案在react和vue下有太大的差异。如果后面我在工作中有使用vue去做的机会,在更新一版使用vue的demo把。最重要的还是要去理解这个权限,其实也是简单的东西。希望能够帮助到有需要的人。

具体实现肯定不是几行代码能够贴清楚的,代码怎么实现也关系到具体的项目结构。

我提供了demo,上传到了github上,地址为:admin-permission-demo

demo也是根据我自己搭的管理系统模板做的,地址为:antd-admin