ant-design-pro 4.x 权限控制剖析

ant-design-pro 5.0 发布之际,把 4.x 的权限控制原理梳理了一遍。

 

第一部分,页面加载,生成控制权限的组件 Authorized (@/utils/Authorized.ts),获取当前权限。

1. 程序一加载,传递 getAuthority() 返回的 currentAuthority,调用 RenderAuthorize 函数,返回 Authorized 权限控制组件(返回值说明见第二步)

// src/utils/Authorized.ts

import RenderAuthorize from '@/components/Authorized';
import { getAuthority } from './authority';

let Authorized = RenderAuthorize(getAuthority());

 

2. 第一步中 引入的 RenderAuthorize 为一个函数, 接收当前权限,currentAuthority 作为参数,返回 Authorized 权限控制组件; 因此,上一步中,let Authorized = RenderAuthorize(getAuthority()); 变量 Authorized 即为 src/components/Authorized/Authorized.ts 组件,同时 通过调用 RenderAuthorize,对 当前权限 CURRENT 做了更新。

// src/components/Authorized/index.ts

import Authorized from './Authorized'; // 控制权限的包裹组件
...
import renderAuthorize from './renderAuthorize';

...
const RenderAuthorize = renderAuthorize(Authorized); // 根据下面的代码可知,renderAuthorize(Authorized) 的调用结果为一个函数 

export default RenderAuthorize;

------------------------------------------------------------------------


// src/components/Authorized/renderAuthorize.ts

let CURRENT: string | string[] = 'NULL';

const renderAuthorize = (Authorized) => (
  currentAuthority: CurrentAuthorityType,
) => {
  if (currentAuthority) {
    ...
  } else {
    CURRENT = 'NULL';
  }
  return Authorized;
};

export default <T>(Authorized: T) => renderAuthorize<T>(Authorized);

  

第二部分,根据权限展示或隐藏菜单。

1.  通过 BasicLayout 的 menuDataRender 属性, 控制菜单是否显示。 

Authorized.check 校验路由的权限,authority 是否有至少一项包含在当前用户权限 currentAuthority 中。返回传入路由,或 null。

// src/layouts/BasicLayout.ts

const menuDataRender = (menuList: MenuDataItem[]): MenuDataItem[] => {
  return menuList.map((item) => {
    const localItem = {
      ...item,
      children: item.children ? menuDataRender(item.children) : undefined,
    };
    return Authorized.check(item.authority, localItem, null) as MenuDataItem;
  });
};

  

2. 调用 check 函数,就是调用 checkPermissions 函数,第二个参数为第一部分中,附上值的 CURRENT。

// src/components/Authorized/CheckPermissions.tsx

import { CURRENT } from './renderAuthorize';

const checkPermissions = <T, K>(
  authority: IAuthorityType,
  currentAuthority: string | string[],
  target: T,
  Exception: K,
): T | K | React.ReactNode => {
  // 没有判定权限.默认查看所有
  // 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;
  }
  ...
  throw new Error('unsupported parameters');
};

function check(authority: IAuthorityType, target: T, Exception: K): T | K | React.ReactNode {
  return checkPermissions<T, K>(authority, CURRENT, target, Exception);
}

  

第三部分,路由权限控制。

1. 获取跟 pathname 匹配的路由

// src/utils/utils.ts
// 返回跟 pathname 匹配的路由
export const getAuthorityFromRouter = <T extends Route>(
  router: T[] = [],
  pathname: string,
): T | undefined => {
  const authority = router.find(
    ({ routes, path = '/', target = '_self' }) =>
      (path && target !== '_blank' && pathRegexp(path).exec(pathname)) ||
      (routes && getAuthorityFromRouter(routes, pathname)),
  );
  if (authority) return authority;
  return undefined;
};

2. 获取当前 url 匹配的路由后,传入该路由的 authority,通过 Authorized 组件包裹 children 校验权限。

// src/layouts/BasicLayout.tsx
import { getAuthorityFromRouter } from '@/utils/utils';

const BasicLayout = () => {
  ...
  const authorized = getAuthorityFromRouter(props.route.routes,     
  location.pathname || '/') || {
    authority: undefined,
  };

  return (
  <ProLayout>
    ...
    <Authorized authority={authorized!.authority} noMatch={noMatch}>
        {children}
    </Authorized>
  </ProLayout>
);
};

3. Authorized 组件内部,也通过 check 函数校验权限

// src/components/Authorized/Authorized.tsx
import check, { IAuthorityType } from './CheckPermissions';

const Authorized: React.FunctionComponent<AuthorizedProps> = ({
  children,
  authority,
  noMatch = (
    <Result
      status="403"
      title="403"
      subTitle="Sorry, you are not authorized to access this page."
    />
  ),
}) => {
  const childrenRender: React.ReactNode = typeof children === 'undefined' ? null : children;
  const dom = check(authority, childrenRender, noMatch);
  return <>{dom}</>;
};

export default Authorized as IAuthorizedType;

 

posted @ 2021-07-05 00:00  张大鱼  阅读(569)  评论(0)    收藏  举报