lihroff / blog

💼此项目使用Issues来归档记录博客文章 (个人Blog、翻译、转载)

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

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组件。

AuthorizedSecuredAuthorizedRoutecheck是什么?有什么区别?

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;

AuthorizedRouteAuthorized类似,区别在于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的权限组件

image

上图中在配置路由中传入了2个额外的属性Routesauthority, 他们是干什么用的呢?

看出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;
  }

由此看出:

  1. const { Routes } = route; 从每个route对象上读取Routes属性
  2. 通过 while 循环嵌套 外部传入Routes权限组件,而传入authority属性通过spread operater 传入到Routes权限组件这就是权限组件的准入权限的列表。

大致就是这样在配置上实现了权限控制。