在项目中经常有的场景是不同的用户的权限不同,通常有如下场景:
针对这些场景,我们为中台场景下常用的权限控制提供了一种更加简单、易用、通用的解决方案。实现了一个基于 umi 插件的权限管理方案 - @umijs/plugin-access。通过定义权限,使用权限,完成 React 组件内的执行权限控制,渲染权限控制。搭配 @alipay/umi-plugin-layout 插件一起使用,还可以进一步完成对路由权限的控制。
权限的定义依赖于初始数据,初始数据需要通过 @umijs/plugin-initial-state 生成。
生成完初始化数据后,就可以开始定义权限了。首先新建 src/access.ts
,在该文件中 export default
一个函数,定义用户拥有的权限,以下是示例定义:
// src/access.tsexport default function (initialState) {return {canReadFoo: true,canUpdateFoo: () => true,canDeleteFoo: (data) => data?.status < 1, // 按业务需求自己任意定义鉴权函数};}
该文件需要返回一个 function,返回的 function 会在应用初始化阶段被执行,执行后返回的对象将会被作为用户所有权限的定义。对象的每个 key 对应一个 boolean 值,只有 true 和 false,代表用户是否有该权限。
其中的 initialState
来自于全局初始化数据,你可以基于这些数据来初始化用户权限。
【注意】使用 useAccess, Access 前,需要在 config/config.ts 存在配置 access: {},,其他配置详见 @umijs/plugin-access
使用示例如下:
import React from 'react';import { useAccess, Access } from 'umi';const PageA = (props) => {const { foo } = props;const access = useAccess(); // access 实例的成员: canReadFoo, canUpdateFoo, canDeleteFooif (access.canReadFoo) {// 任意操作}return (<div><Access accessible={access.canReadFoo} fallback={<div>Can not read foo content.</div>}>Foo content.</Access><Access accessible={access.canUpdateFoo()} fallback={<div>Can not update foo.</div>}>Update foo.</Access><Access accessible={access.canDeleteFoo(foo)} fallback={<div>Can not delete foo.</div>}>Delete foo.</Access></div>);};
你可以通过 useAccess
hook 来获取权限定义,另外我们内置了 Access
组件用于页面的元素显示和隐藏的控制。
Access
组件只有 hooks 的用法,如果需要在 class 组件中使用的话,可以把需要用到权限的拆分为 function。
示例如下:
const Button=()=>{const access = useXX();// 权限处理return <Button/>}
如果需要对路由还有菜单进行权限控制,可以直接在路由上原有基础配置上加上权限控制相关的属性,即可快速实现路由和菜单的权限控制。(前提需要使用最佳实践的 Layout 方案 - @alipay/umi-plugin-layout )。
在以上定义(src/access.ts
, src/app.ts
)完成的基础上,再在路由配置项上添加 access
属性即可完成路由和菜单的权限控制。access
属性的值为 src/access.ts
中返回的对象的 key。以下为实际例子:
假设权限定义文件 src/access.ts
内容如下:
// src/access.tsexport default function (initialState = {}) {const { isAdmin, hasRoutes = [] } = initialState;return {// ...adminRouteFilter: () => isAdmin, // 只有管理员可访问normalRouteFilter: (route) => hasRoutes.includes(route.name), // initialState 中包含了的路由才有权限访问};}
通过以上示例可以看到,权限路由控制相关的函数,接收"当前处理的路由"作为第一个参数
那么只需要按以下方式在常规路由配置中加上 access
这一项即可:
// config/config.tsimport { defineConfig } from 'umi';export default defineConfig({routes: [{path: '/foo',name: 'foo',// ...access: 'normalRouteFilter', // 会调用 src/access.ts 中返回的 normalRouteFilter 进行鉴权},{path: '/admin',name: 'admin',// ...access: 'adminRouteFilter', // 会调用 src/access.ts 中返回的 adminRouteFilter 进行鉴权},],// ...});
对应鉴权函数(比如 adminRouteFilter
)在接收路由作为参数后返回值为 false
,该条路由将会被禁用,并且从左侧 layout 菜单中移除,如果直接从 URL 访问对应路由,将看到一个 403 页面。