ant-design-pro 与 umi 相辅相成的授权实现
lihroff opened this issue · comments
在ant-design-pro(antpro)中有一系列的Authrize
组件他们是antpro里授权的实现其中包含多种使用场景,而umi框架中使用配置式路由内置提供了特定的属性Routes
能在配置路由时集成权限控制。
下面会参数关于antpro和umi细节:
antpro中的权限组件(Authrize)
权限组件,通过比对现有权限与准入权限,决定相关元素(子组件 \ noMatch组件)的展示。
Authrize组件的入口
查看Authrize的入口index.js
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);
从源码可以看到其导入了5个对象。前面4个都对应着权限组件的不同使用场景,其中Authorized
是最常用的其他3个都作为属性挂载在Authorized
。最后导入的是renderAuthorize
, 然后默认导出renderAuthorize(Authorized)
renderAuthorize
那么renderAuthorize
是干什么用的呢?
let CURRENT = 'NULL';
/**
* use authority or getAuthority
* @param {string|()=>String} currentAuthority
*/
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;
};
export { CURRENT };
export default Authorized => renderAuthorize(Authorized);
由源码可以看出renderAuthorize
是一个高阶函数, 在第一次调用时传入Authorized
,再次调用时传入currentAuthority
此参数代表当前[用户]拥有权限(当currentAuthority
为函数此时会执行函数将返回结果赋值到CURRENT
, 当currentAuthority
为数组或者字符串则直接赋值到CURRENT
, 否者CURRENT
为字符串NULL,而这个CURRENT
就缓冲这当前[用户]拥有权限, 可以从该文件导出使用),最后执行返回第一次调用的Authorized
组件。
Authorized
、Secured
、 AuthorizedRoute
、 check
是什么?有什么区别?
Authorized
import CheckPermissions from './CheckPermissions';
const Authorized = ({ children, authority, noMatch = null }) => {
const childrenRender = typeof children === 'undefined' ? null : children;
return CheckPermissions(authority, childrenRender, noMatch);
};
export default Authorized;
由源码可以看出Authorized就是一个纯函数组件它接受三个属性children, authority, noMatch
,然后返回CheckPermissions
的执行结果。children:表示权限判断通过时展示什么,authority:表示展示children需要那些权限(之一),noMatch:表示权限判断失败时展示什么
AuthorizedRoute
import React from 'react';
import { Route, Redirect } from 'umi';
import Authorized from './Authorized';
const AuthorizedRoute = ({ component: Component, render, authority, redirectPath, ...rest }) => (
<Authorized
authority={authority}
noMatch={<Route {...rest} render={() => <Redirect to={{ pathname: redirectPath }} />} />}
>
<Route {...rest} render={props => (Component ? <Component {...props} /> : render(props))} />
</Authorized>
);
export default AuthorizedRoute;
AuthorizedRoute
和Authorized
类似,区别在于noMatch使用Route
render Redirect
到其他路由,权限判断通过时也是由Route
包裹。
Secured
而Secured
差不多是Authorized
到另一种用法,例如:
const { Secured } = RenderAuthorized('user');
@Secured('admin')
class TestSecuredString extends React.Component {
render() {
return (
<Alert message="user Passed!" type="success" showIcon />
)
}
}
Secured
将作为类修饰符使用,参入到参数就是Authorized
中到authority
参数。
check
import React from 'react';
import PromiseRender from './PromiseRender';
import { CURRENT } from './renderAuthorize';
/**
* 通用权限检查方法
* 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') {
try {
const bool = authority(currentAuthority);
// 函数执行后返回值是 Promise
if (bool instanceof Promise) {
return <PromiseRender ok={target} error={Exception} promise={bool} />;
}
if (bool) {
return target;
}
return Exception;
} catch (error) {
throw error;
}
}
throw new Error('unsupported parameters');
};
export { checkPermissions };
const check = (authority, target, Exception) =>
checkPermissions(authority, CURRENT, target, Exception);
export default check;
由源码可处导出check
方法接受的参数与Authorized
接受的属性相同
authority === this.props.authority
target === this.props.children
Exception === this.props.noMatch
check
方法直接返回调用checkPermissions
的结果。那么checkPermissions
是什么,这就是antpro中如何实现准入权限
与现有权限
的判断,最后导出渲染不同的结果。
umi如何辅佐antpro的权限组件
上图中在配置路由中传入了2个额外的属性Routes
与authority
, 他们是干什么用的呢?
看出umi源文件umi/packages/umi/src/renderRoutes.js
,由如下代码:
function withRoutes(route) {
if (RouteInstanceMap.has(route)) {
return RouteInstanceMap.get(route);
}
const { Routes } = route;
let len = Routes.length - 1;
let Component = args => {
const { render, ...props } = args;
return render(props);
};
while (len >= 0) {
const AuthRoute = Routes[len];
const OldComponent = Component;
Component = props => (
<AuthRoute {...props}>
<OldComponent {...props} />
</AuthRoute>
);
len -= 1;
}
由此看出:
const { Routes } = route;
从每个route对象上读取Routes属性- 通过
while
循环嵌套 外部传入Routes
权限组件,而传入authority
属性通过spread operater 传入到Routes
权限组件这就是权限组件的准入权限的列表。
大致就是这样在配置上实现了权限控制。