React 组件权限控制的实现

目录
  • 前话
  • 正文
  • 1. 控制方式
    • 1.1 直接计算
    • 1.2 通用权限Hoc
    • 1.3 权限包裹组件
  • 2. 控制结果
    • 2.1 显隐控制
    • 2.2 自定义渲染
  • 3. 权限数据
    • 3.1 静态权限
    • 3.2 动态权限

前话

具备用户体系的业务系统往往需要具备权限控制的能力。前后不分离的开发模式权限控制主要由后端结合模版引擎实现,而前后分离的开发模式由前后两端协商权限配置分别进行数据权限控制和组件权限控制。

正文

权限配置格式不限,为进行后续演示本篇设定权限配置如下:

export type IAuthConfig = {
  /** 权限标识列表 */
  keys: string[];
};

且提供一个Hook直接获取当前用户信息:

export type IUser = {
  /** 权限配置 */
  authConfig: IAuthConfig;
};

/** 用户信息Hook */
export function useUser() {
  // ...略
  return user;
}

首先我们先定义一个权限Hook,内容如下:

/**
 * 转换为权限标识数组
 * @param auth 权限标识
 * @returns
 */
function getAuthKeys(auth: string | string[]) {
  return Array.isArray(auth) ? auth : [auth];
}

/** 权限鉴权类型 */
export enum EAuthType {
  /** 或权限(至少匹配一个) */
  "some" = "some",
  /** 与权限(全部匹配) */
  "every" = "every",
}

/** 权限Hook */
export function useAuth() {
  const { authConfig } = useUser();

  // 权限标识列表
  const authKeys = useMemo(() => authConfig?.keys ?? [], [authConfig]);
  // 校验是否具备权限
  const hasAuth = useCallback(
    (auth: string | string[], type?: EAuthType) =>
      getAuthKeys(auth)[type ?? EAuthType.every]((key) =>
        authKeys.includes(key)
      ),
    [authKeys]
  );

  const ret: [typeof authKeys, typeof hasAuth] = [authKeys, hasAuth];
  return ret;
}

1. 控制方式

对于前端开发而言一般只需做组件控制,控制方式有很多种写法。

1.1 直接计算

const Cmpt: React.FC = () => {
  const [, hasAuth] = useAuth();
  // 计算是否有权限
  const authorized = useMemo(() => hasAuth("test"), [hasAuth]);

  // ...略
};

1.2 通用权限Hoc

export function withAuth<P extends Record<string, unknown> = {}>(
  auth: string | string[],
  type?: EAuthType
) {
  return function (Component: any) {
    const WrappedComponent: React.FC<P> = (props) => {
      const [, hasAuth] = useAuth();

      const instance = React.createElement(Component, {
        ...props,
      });

      // 计算是否有权限
      const authorized = hasAuth(auth, type);

      // ...略
    };

    WrappedComponent.displayName = `withAuth(${getDisplayName(Component)})`;

    return WrappedComponent;
  };
}

1.3 权限包裹组件

const AuthWrapper: React.FC<IProps> = ({ auth, type, children }) => {
  const [, hasAuth] = useAuth();
  // 计算是否有权限
  const authorized = useMemo(() => hasAuth(auth, type), [auth, type, hasAuth]);

  // ...略
};

2. 控制结果

常见控制结果为控制组件的显示或隐藏,或根据是否具备权限做组件的自定义渲染。

为方便演示后面统一使用权限包裹组件进行说明。

2.1 显隐控制

具备权限的组件直接渲染,否则返回null,即可实现显隐控制。权限包裹组件实现如下:

const AuthWrapper: React.FC<IProps> = ({ auth, type, children }) => {
  const [, hasAuth] = useAuth();
  // 计算是否有权限
  const authorized = useMemo(() => hasAuth(auth, type), [auth, type, hasAuth]);
  // 具备权限的组件直接渲染,否则返回null
  return authorized ? children : null;
};

在需要权限控制的组件外包裹权限组件即可。

const Cmpt: React.FC = () => {
  <>
    <AuthWrapper auth="test">
      <Button>Hello World</Button>
    </AuthWrapper>
  </>;
};

2.2 自定义渲染

实际业务场景中,存在需要提醒用户没有权限的情况,这时需要权限包裹组件支持自定义渲染,实现方式有多种:

  • 静态props注入
  • 动态props映射
  • render props

相比较之下render props更为自由,权限包裹组件完善后实现如下:

type IProps = {
  /** 权限标识 */
  auth: string | string[];
  /** 鉴权类型 */
  type?: EAuthType;
  /** 子内容 */
};

const AuthWrapper: React.FC<IProps> = ({ auth, type, children }) => {
  const [, hasAuth] = useAuth();
  // 计算是否有权限
  const authorized = useMemo(() => hasAuth(auth, type), [auth, type, hasAuth]);

  // 自定义渲染
  if (typeof children === "function") {
    return children(authorized);
  }

  // 显隐控制
  return authorized ? children : null;
};

这时就可以渲染提示无权限组件:

const Cmpt: React.FC = () => {
  <>
    <AuthWrapper auth="test">
      {(authorized) => <Button disabled={!authorized}>Hello World</Button>}
    </AuthWrapper>
  </>;
};

3. 权限数据

前端开发做组件控制的颗粒度取决于权限数据的类型,权限数据类型分为静态权限和动态权限。
其中静态权限一般在完成登录认证后即可一次性获取,而动态权限则在操作数据时进行权限校验。
因此不难发现静态权限往往用于控制路由、页面或者粗糙的操作权限。而动态权限则能够对动态数据进行更细粒度的操作权限控制(需后端数据权限控制能力配合)。

3.1 静态权限

如前面描述,登录认证后即可从用户信息中得到权限标识,同样前面的栗子均也为静态权限示例。

3.2 动态权限

需要动态权限校验的场景在业务系统中也较为常见,例如用户能够看到列表数据,但禁止查阅无权限的数据详情。
由此可知对于用户而言,获取的动态的列表数据需要逐一进行权限校验,这时我们对权限Hook和包裹组件进行改造,改造后代码如下:

type IAuthDynamicConfig = {
  // ...略
};

/** 权限Hook */
export function useAuth() {
  // ...略

  // 动态校验是否具有权限
  const dynamicAuth = useCallback(
    (auth: string | string[], type?: EAuthType, config?: IAuthDynamicConfig) =>
      // 批量异步校验权限,实现略
      append2DynamicAuthTask(auth, type, config),
    []
  );

  const ret: [typeof authKeys, typeof hasAuth, typeof dynamicAuth] = [
    authKeys,
    hasAuth,
    dynamicAuth,
  ];
  return ret;
}

/** 权限包裹组件 */
const AuthWrapper: React.FC<IProps> = ({ auth, type, config, children }) => {
  const [, hasAuth, dynamicAuth] = useAuth();

  const [authorized, setAuthorized] = useState(false);

  // 计算是否有权限
  useEffect(() => {
    if (config === undefined) {
      setAuthorized(hasAuth(auth, type));
      return;
    }
    dynamicAuth(auth, type, config).then(setAuthorized);
  }, [auth, type, config, hasAuth, dynamicAuth]);

  // ...略
};

使用方式如下:

const Cmpt: React.FC = () => {
  // ...略

  <>
    {data.map((item) => (
      <div key={item.id}>
        <div>{item.title}</div>
        <div>
          <AuthWrapper
            auth="detail"
            config={{
              module: "demo",
              identify: item.id,
            }}
          >
            {(authorized) => (
              <Button disabled={!authorized} onClick={handleDetail}>
                详情
              </Button>
            )}
          </AuthWrapper>
        </div>
      </div>
    ))}
  </>;
};

到此这篇关于React 组件权限控制的实现的文章就介绍到这了,更多相关React 组件权限控制内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • react实现菜单权限控制的方法

    通常公司的后台管理系统都需要权限控制,即不同的角色用户看到不同的菜单,如下图: 下面,通过react实现这样的后台管理系统(脚手架),功能简介: 1.顶部的菜单项根据用户的角色动态生成. 2.侧边测菜单项根据已选的顶部菜单动态生成. 直接上代码: 路由配置: export default ( <Route path="/" component={App}> <IndexRoute component={EmployeeList}/> <Route path

  • react高阶组件经典应用之权限控制详解

    前言 所谓高级组件,即:接受一个组件作为参数,并且其返回值也为一个react组件 而大家应该都知道,权限控制算是软件项目中的常用功能了.在网站中,权限控制一般分为两个维度:页面级别和页面元素级别. 我们来说说页面元素粒度的权限控制.在某个页面中,有个"创建用户"的按钮,管理员才能看到. 一般想到的做法类似这样 class Page extends Component{ render() { let hasCreatePermission = tool.getAuth("cre

  • react router4+redux实现路由权限控制的方法

    总体概述 一个完善的路由系统应该是这样子的,当链接到的组件是需要登录后才能查看,要能够跳转到登录页,然后登录成功后又跳回来之前想访问的页面.这里主要是用一个权限控制类来定义路由路由信息,同时用redux把登录成功后要访问的路由地址给保存起来,登录成功时看redux里面有没有存地址,如果没有存地址就跳转到默认路由地址. 路由权限控制类 在这个方法里面,通过sessionStorage判断是否登录了,如果没有登录,就保存一下当前想要跳转的路由到redux里面.然后跳转到我们登录页. import R

  • React 组件权限控制的实现

    目录 前话 正文 1. 控制方式 1.1 直接计算 1.2 通用权限Hoc 1.3 权限包裹组件 2. 控制结果 2.1 显隐控制 2.2 自定义渲染 3. 权限数据 3.1 静态权限 3.2 动态权限 前话 具备用户体系的业务系统往往需要具备权限控制的能力.前后不分离的开发模式权限控制主要由后端结合模版引擎实现,而前后分离的开发模式由前后两端协商权限配置分别进行数据权限控制和组件权限控制. 正文 权限配置格式不限,为进行后续演示本篇设定权限配置如下: export type IAuthConf

  • 基于Vue实现后台系统权限控制的示例代码

    用Vue/React这类双向绑定框架做后台系统再适合不过,后台系统相比普通前端项目除了数据交互更频繁以外,还有一个特别的需求就是对用户的权限控制,那么如何在一个Vue应用中实现权限控制呢?下面是我的一点经验. 权限控制是什么 在权限的世界里服务端提供的一切都是资源,资源可以由请求方法+请求地址来描述,权限是对特定资源的访问许可,所谓权限控制,也就是确保用户只能访问到被分配的资源.具体的说,前端对资源的访问通常是由界面上的按钮发起,比如删除某条数据:或由用户进入某一个页面发起,比如获取某个列表数据

  • Yii2搭建后台并实现rbac权限控制完整实例教程

    1.安装yii2 未安装的请参考yii2史上最简单式安装教程,没有之一 已安装的请继续看下一步操作 2.配置数据库 2.1 配置数据库 修改common/config/main-local.php 实际项目中本地的数据库往往跟线上数据库不一致, 我们这里配置到main-local.php就可以了,产品上线后,我们可以使用git或者svn忽略掉main-local.php,线上直接部署. 我们这里使用的mysql数据库,配置如下 当然啦,上面红圈圈的信息需要你自己手动修改掉,要是十分巧合跟我的一样

  • Struts2通过自定义标签实现权限控制的方法

    近期在开发中遇到一种需求:根据用户的权限决定是否显示某操作按钮. 例如:若用户拥有删除数据的权限,则在界面中显示"删除"按钮:若用户无该权限,则界面中不显示相应按钮. 这样,就需要用到自定义标签了. 要定义Struts2的自定义标签,只需三步: 1.定义一个Component类,并继承自org.apache.struts2.components.Component; 2.定义一个Tag类,并继承自import org.apache.struts2.views.jsp.Component

  • 深入浅析Yii admin的权限控制

    说到CMS,最需要有的东西就是权限控制,特别是一些复杂的场景,多用户,多角色,多部门,子父级查看等等.最近在开发一个线下销售的东东,这个系统分为管理员端,省代端,客户端,门店端,销售端, 部门端,部门老大下面分子部门等等,恶心的需求.我们这个项目使用yii框架开发,yii在php届还是比较流行的,虽然说laravel现在横行,但是一些部门一些团队还是采用了yii框架,比如我们. 我是刚接触yii这个框架,开始的时候对这种面向组件的框架甚是别扭.当时打算自己写权限的,自己创建权限表,关联表等,但是

  • Vue2.0用户权限控制解决方案

    Vue-Access-Control是一套基于Vue/Vue-Router/axios 实现的前端用户权限控制解决方案,通过对路由.视图.请求三个层面的控制,使开发者可以实现任意颗粒度的用户权限控制. 安装 版本要求 Vue 2.0x Vue-router 3.x 获取 git:git clone https://github.com/tower1229/Vue-Access-Control.git npm:npm i vue-access-control 运行 //开发 npm run dev

  • Vue-Access-Control 前端用户权限控制解决方案

    Vue-Access-Control是一套基于Vue/Vue-Router/axios 实现的前端用户权限控制解决方案,通过对路由.视图.请求三个层面的控制,使开发者可以实现任意颗粒度的用户权限控制. 整体思路 会话开始之初,先初始化一个只有登录路由的Vue实例,在根组件created钩子里将路由定向到登录页,用户登录成功后前端拿到用户token,设置axios实例统一为请求headers添加{"Authorization":token}实现用户鉴权,然后获取当前用户的权限数据,主要包

随机推荐