乐趣区

关于前端:react-权限控制方案实践

react 权限管制计划实际

权限管制是我的项目中,特地是后盾治理我的项目中比拟常见的性能了
结合实际的我的项目需要,讲讲在 react 中是如何实现权限管制的

背景

  1. 我的项目应用 umi 搭建
  2. 需要:

    1. 依据不同角色权限配置路由权限
    2. 依据不同权限管制页面显示成果
    3. 按钮显示暗藏

实现页面路由权限

实现成果:无权限的用户没有该页面的入口:左侧菜单无入口,以及间接进入 url 提醒无权限

@umijs/plugin-access

https://umijs.org/zh-CN/plugi…
配合@umijs/plugin-access 插件应用

src/access.ts

约定 src/access.ts 文件为权限定义文件,该文件须要默认导出一个办法,导出的办法会在我的项目初始化时被执行。该办法须要返回一个对象,对象的每一个值就对应定义了一条权限。具体的介绍见文档。

计划 1

我的需要是依据用户角色来判断是否有该路由权限,如果依照文档的 demo 来,那么我须要先:

  1. 定义每个角色是否有 pageA 权限,pageB 权限……
  2. 再在 access.ts 里输入对象 {canReadPageA: true, canReadPageB: false...}
  3. 而后再在路由文件里定义 access: 'canReadPageA', access: 'canReadPageB'……

这样尽管能够实现,但代码量太大,要对每个须要权限的页面进行定义和判断,access.ts 和 route.ts 文件都须要嵌入大量代码

所以我扭转了思路,换了另一种计划,也就是计划 2

计划 2

access.ts 中,当返回的对象中,值是办法时:

  1. 参数是 route, 即是以后路由信息
  2. 办法最初返回布尔值

利用这个参数,就能够在 route 里退出咱们所须要的信息

// routes.ts
[
  {
    name: 'pageA',
    path: '/pageA',
    component: './pageA',
    access: 'auth', // 权限定义返回值的某个 key
    roles: ['admin', 'user'], // role 为 admin 或者 user 时能够拜访 pageA 页面
  },
  {
    name: 'pageB',
    path: '/pageB',
    component: './pageB',
    access: 'auth',
    roles: ['admin'],// 只有 role 为 admin 时能够拜访 pageA 页面
  },
]

我给 route 配置了两个属性:

  1. access 值为 access.ts 返回对象的某个 key,这里的话固定为 auth
  2. roles 定义能够有该页面权限的角色组

access.ts 中返回 key 为 auth 的对象:

// access.ts
let hasAuth = (route: any, roleId?: string) => {
  //  要害:比照 route.roles 和 currentUser.roleId 判断是否有权限
  return route.roles ? route.roles.includes(roleId) : true;
};
export default function access(initialState: { currentUser?: API.CurrentUser | undefined}) {const { currentUser} = initialState || {};
  return {auth: (route: any) => hasAuth(route, currentUser?.roleId),
  };
}

拿到 route 里的信息和以后用户信息进行比照,判断,返回布尔值

比照计划 1,计划 2 长处就是,之后新增页面时,只须要在 routes.ts 里定义好该页面的 accessroles 属性, 不须要改变到 access.ts

实现权限管制页面显示

实现成果:用户有菜单入口,进入页面后显示无权限

计划 1

思路:

  1. 利用 umi 提供的 Access 组件来实现
  2. 依据 currentUser 对应的字段来判断是否有权限
    代码如下:

    import {Access} from 'umi';
    
    <Access accessible={currentUser.foo} fallback={<div> 暂无权限 </div>}>
      Foo content.
    </Access>;

    毛病:须要在对应的页面中嵌入代码

计划 2

思路:

通过高阶组件 wrappers 实现

  1. routes.ts 配置 wrappers 属性

    // routes.ts
    [
      {
     name: 'pageA',
     path: '/pageA',
     component: './pageA',
     wrappers: ['@/wrappers/authA']
      },
      {
     name: 'pageB',
     path: '/pageB',
     component: './pageB',
     wrappers: ['@/wrappers/authB']
      },
    ]

    这样,拜访 /pageA 时,会先通过 @/wrappers/authA 做权限校验

  2. 而后在 @/wrappers/authA 中,
// wrappers/authA
import {useModel} from 'umi';

export default (props) => {const { initialState} = useModel('@@initialState');
  const {currentUser} = initialState || {};
  if (currentUser.authA) {return <div>{ props.children}</div>;
  } else {return <div> 无权限 </div>;}
}

这样,依据 currentUser 来判断是否渲染组件

计划 2 的长处是:

毋庸在页面组件中嵌入相干代码,只须要在 routes.tx 中配置 wrappers,鉴权局部由 @/wrappers/ 来解决

然而,毛病就是,如果有多个权限,如 authA, authB, authC…… 那么则须要在 @/wrappers/ 新建多个鉴权文件

实现按钮权限

实现成果:
无权限的按钮不显示或者置灰

个别的做法是在组件中判断

// 不显示按钮
{currentUser.auth ?  <button> 创立 </button> : null}
// 置灰
{<button disabled={currentUser.auth}> 创立 </button>}

但如果有大量的权限按钮,那么将要写好屡次这种代码,所以在这里对按钮进行一次封装

// AuthBtn
import React, {useState, useEffect, useRef} from 'react';
import {Button} from 'antd';

const AuthBtn: React.FC<{}> = (props) => {let { authId, children} = props;
  // btnIds 应该有后盾接口返回,通知前端用户有哪些按钮权限
  let btnIds = ['read', 'edit'];
  let hasAuth = btnIds.includes(authId);
  // 这里能够依据理论需要封装
  return <Button disabled={!hasAuth}>{children}</Button>;
};
export default AuthBtn;

// index.ts
<AuthBtn authId="read">read 只读权限 </AuthBtn>
<AuthBtn authId="write">write 写入权限 </AuthBtn>

传入的 authId 须要先和后盾约定好, 还能够依据理论需要传入 type、loading 等

这样,一般按钮用 Button, 须要鉴权的应用 AuthBtn

总结

以上就是最近对于权限管制的实际,做到对路由、页面和按钮层级进行鉴权,每一种都有对应的实现计划,每个计划都有本人的优缺点,旨在更优雅地编程!
如果有更好的计划,欢送评论区留言!

退出移动版