前言
这段时间一直用ant design pro这个框架开发项目,其中最重要的模块之一应该就是其自带的权限控制功能,现在也来谈谈我对这个模块代码的理解。随着框架的升级,代码部分可能有所改动,但其实现思路和方法应是没有大的变动。
权限管理
config/config.js
首先可以看到只需在config文件中进行路由配置,即能访问对应路由页面
export default {
...
routes: [
{
path: '/user',
component: '../layouts/UserLayout',
routes: [
{
name: 'login',
path: '/user/login',
component: './user/login',
},
],
},
...
}
为了统一管理路由配置,我一般单独封装router.config.js文件,通过es6的export import最终配置到config.js文件
router.config
接着看看官网上的应用实例
{
path: '/',
component: '../layouts/BasicLayout',
Routes: ['src/pages/Authorized'],
authority: ['admin', 'user'],
routes: [
// forms
{
path: '/form',
icon: 'form',
name: 'form',
routes: [
{
path: '/form/basic-form',
name: 'basicform',
component: './Forms/BasicForm',
},
{
path: '/form/advanced-form',
name: 'advancedform',
authority: ['admin'], //配置准入权限,可以配置多个角色
component: './Forms/AdvancedForm',
},
],
},
],
}
可以明白只需在路由配置中设置Routes属性,pro 的路由系统中会默认包裹Authorized进行判断处理,而authority属性则代表准入权限组合
Authorize 组件
来到src/pages/Authorize.jsx文件,可以看到它导出的组件包裹了Authorized组件
export default ({ children }) => (
<Authorized
authority={children.props.route.authority}
noMatch={<Redirect to="/user/login" />}
>
{children}
</Authorized>
);
authority属性传入的时该路由页面准入的权限组合,noMatch属性则是传入当用户无访问权限时显示的页面组件。进一步你可以根据用户是否已登录,来控制是跳转到登录页或是一个403错误页面
<Authorized
authority={getRouteAuthority(location!.pathname, routes)!}
noMatch={isLogin ? <Redirect to="/exception/403" /> : <Redirect to="/user/login" />}
>
{children}
</Authorized>
往上可以看到Authorized组件的由来
import RenderAuthorized from '@/components/Authorized'; // eslint-disable-line
import { getAuthority } from '@/utils/authority';
const Authority = getAuthority();
const Authorized = RenderAuthorized(Authority);
其中getAuthority方法理论上返回当前用户具有的所有权限,可以依照实际需求在src/utils/authority.js文件中对方法改写
所以Authorized是RenderAuthorized方法执行后得到的组件,同时方法参数是Authority,即用户所有权限
RenderAuthorized
来到src/components/Authorized/index.jsx
import Authorized from './Authorized';
import AuthorizedRoute from './AuthorizedRoute';
import Secured from './Secured';
import check from './CheckPermissions';
import renderAuthorize from './renderAuthorize';
Authorized.Secured = Secured;
Authorized.AuthorizedRoute = AuthorizedRoute;
Authorized.check = check;
export default renderAuthorize(Authorized);
可以看到它导出的是renderAuthorize(Authorized)执行后的结果,这个结果理论上也是一个方法,它接收的参数是当前用户所有的权限组合,即getAuthority()得到的数据,最后返回一个组件
renderAuthorize
接着先看renderAuthoriz方法
...
export default Authorized => renderAuthorize(Authorized);
可以看到它导出的是一个方法,所以在index.jsx文件的renderAuthorize(Authorized)返回的最终是renderAuthorize(Authorized)执行得到的结果,往上看
const renderAuthorize = Authorized => currentAuthority => {
if (currentAuthority) {
if (typeof currentAuthority === 'function') {
CURRENT = currentAuthority();
}
if (
Object.prototype.toString.call(currentAuthority) === '[object String]' ||
Array.isArray(currentAuthority)
) {
CURRENT = currentAuthority;
}
} else {
CURRENT = 'NULL';
}
return Authorized;
};
可以看到renderAuthorize(Authorized)执行得到的是一个组件,它接收currentAuthority属性,并且渲染的是外层接收的Authorized参数
由此可以推断这个Authorized参数实际上是一个组件,往上追溯,这个反复提到的Authorized组件是由src\components\Authorized\Authorized.jsx导出
Authorized.jsx
继续看src\components\Authorized\Authorized.jsx
import React from 'react';
import { Result } from 'antd';
import check from './CheckPermissions';
const Authorized = ({
children,
authority,
noMatch = (
<Result
status="403"
title="403"
subTitle="Sorry, you are not authorized to access this page."
/>
),
}) => {
const childrenRender = typeof children === 'undefined' ? null : children;
const dom = check(authority, childrenRender, noMatch);
return <>{dom}</>;
};
export default Authorized;
实际上核心代码是check方法,它返回dom组件,也就是实际的页面组件
CheckPermissions
继续看src\components\Authorized\CheckPermissions.jsx,它的核心代码应该是checkPermissions方法,其中它接收的参数在代码文件也已详细说明
/**
* 通用权限检查方法
* Common check permissions method
* @param { 权限判定 | Permission judgment } authority
* @param { 你的权限 | Your permission description } currentAuthority
* @param { 通过的组件 | Passing components } target
* @param { 未通过的组件 | no pass components } Exception
*/
const checkPermissions = (authority, currentAuthority, target, Exception) => {
// 没有判定权限.默认查看所有
// Retirement authority, return target;
if (!authority) {
return target;
} // 数组处理
if (Array.isArray(authority)) {
if (Array.isArray(currentAuthority)) {
if (currentAuthority.some((item) => authority.includes(item))) {
return target;
}
} else if (authority.includes(currentAuthority)) {
return target;
}
return Exception;
} // string 处理
if (typeof authority === 'string') {
if (Array.isArray(currentAuthority)) {
if (currentAuthority.some((item) => authority === item)) {
return target;
}
} else if (authority === currentAuthority) {
return target;
}
return Exception;
} // Promise 处理
if (authority instanceof Promise) {
return <PromiseRender ok={target} error={Exception} promise={authority} />;
} // Function 处理
if (typeof authority === 'function') {
const bool = authority(currentAuthority); // 函数执行后返回值是 Promise
if (bool instanceof Promise) {
return <PromiseRender ok={target} error={Exception} promise={bool} />;
}
if (bool) {
return target;
}
return Exception;
}
throw new Error('unsupported parameters');
};
这段代码实际上也很好理解,总结来说ant design pro的权限管理模块核心代码就是这个checkPermissions方法,它的作用就是检查路由页面的准入权限和用户所具有的权限是否匹配。如果用户具有访问权限,即返回target参数,即实际的页面组件,否则返回未通过的组件