如何实现前端页面权限和按钮权限

页面权限是后台系统中非常常见的需求,在前端实现页面权限和按钮权限是为了确保用户只能访问其有权访问的页面,并执行其有权执行的操作。本文介绍前端实现页面权限和按钮权限控制的流程和逻辑。

以下的代码以Ant Design Pro 项目为例,其中封装了一些常见的权限处理方式,如路由菜单权限,按钮权限,Access 组件,useAccess 函数等。但是实现原理是类似的,其他框架也可参考该流程实现。

1. 获取用户当前权限并且存在全局状态中

首先需要从后端接口中获取到当前用户拥有的所有权限列表,并且存储在全局状态中(这个场景中使用全局状态库是一个很合适的方式)。

tsx 复制代码
export async function getInitialState() {
  // ...
  const userPermissions = await fetchUserPermissions();
  return {
    // ....
    userPermissions,
  };
}

2. 定义页面权限标记

需要将每个需要权限控制的页面和操作按钮提前定义好权限标记(某个 key),后续中使用用户拥有的权限和这个权限标记对比当前用户是否拥有权限。

这里直接写成一份常量维护,方便在不同组件中使用和维护。

typescript 复制代码
const PERMISSIONS = {
  PAGEA: {
    READ: "PAGEA_READ", // 页面访问权限标记
    ADD: "PAGEA_ADD" // 页面中操作按钮权限标记
    // ... 其他页面中操作按钮
  }
  // ... 其他页面权限标记
}

3. 路由和菜单的权限控制

将之前定义好的权限标记路由和对应的页面上。这里使用里自定义的字段 meta 中的 permission,后续可以直接从当前路由信息中取出这个页面的权限。

typescript 复制代码
export const routes = [
  {
    path: '/pageA',
    component: 'PageA',
    access: 'canAccessRoute', // 会调用 src/access.ts 中返回的 canAccessRoute 进行鉴权
    meta: {
      permission: PERMISSIONS.PAGEA.READ, // 页面访问权限标记
    },
  },
  {
    path: '/pageB',
    component: 'PageB',
    access: 'canAccessRoute', // 会调用 src/access.ts 中返回的 canAccessRoute 进行鉴权
    meta: {
      permission: PERMISSIONS.PAGEB.READ, // 页面访问权限标记
    },
  },
];

编写判断用户是否拥有页面权限判断逻辑,其逻辑十分简单,即判断当前路由权限标记是否在用户拥有的权限中。

typescript 复制代码
export default function access(initialState) {
  const { userPermissions } = initialState ?? {};

  function canAccessRoute(route: IRoute) {
    // 页面对应的权限标记,需要在当前用户拥有的权限中
    if (route.meta?.permission) {
      return userPermissions.includes(route.meta.permission);
    }
    // 无页面权限标记说明改页面不需要权限也能访问
    return true;
  }

  return {
    canAccessRoute,
  };
}

4. 按钮权限控制

按钮权限的控制也可以依照之前页面权限的设计,先定义好操作按钮权限标记,然后判断用户是否拥有该权限。

typescript 复制代码
export default function access(initialState) {
  // ...

  function canAccessAction(permissions: string[]) {
    // 按钮对应的权限标记,需要在当前用户拥有的权限中
    return permissions.every((permission) => userPermissions.includes(permission));
  }

  return {
    // ...
    canAccessAction
  };
}

为操作按钮添加权限控制时,可以使用封装的通用的权限组件,将该组件包裹操作按钮,只需要传入是否拥有权限即可。

注意:除了前端对操作按钮添加权限控制,一般后端也需要在对应接口中根据用户的角色和权限进行校验,返回允许或拒绝的结果。

tsx 复制代码
function PageA() {
  const { canAccessAction } = useAccess();

  return (
   <Access accessible={canAccessAction([PERMISSIONS.PAGEA.ADD])}>
      <Button>新增</Button>
    </Access>
  )
}

封装一个通用的权限组件在 React 中是十分简单的,例如 Umi 中的 Access 组件

tsx 复制代码
export interface AccessProps {
  accessible: boolean;
  fallback?: React.ReactNode;
}
export const Access: React.FC<PropsWithChildren<AccessProps>> = (props) => {
  if (process.env.NODE_ENV === 'development' && typeof props.accessible !== 'boolean') {
    throw new Error('[access] the \`accessible\` property on <Access /> should be a boolean');
  }

  return <>{ props.accessible ? props.children : props.fallback }</>;
};

参考链接

相关推荐
代码搬运媛6 小时前
Jest 测试框架详解与实现指南
前端
counterxing6 小时前
我把 Codex 里的 Skills 做成了一个 MCP,还支持分享
前端·agent·ai编程
wangqiaowq7 小时前
windows下nginx的安装
linux·服务器·前端
之歆7 小时前
DAY_12JavaScript DOM 完全指南(二):实战与性能篇
开发语言·前端·javascript·ecmascript
发现一只大呆瓜7 小时前
Vite凭什么这么快?3分钟带你彻底搞懂 Vite 热更新的幕后黑手
前端·面试·vite
Maimai108087 小时前
React如何用 @microsoft/fetch-event-source 落地 SSE:比原生 EventSource 更灵活的实时推送方案
前端·javascript·react.js·microsoft·前端框架·reactjs·webassembly
kyriewen9 小时前
产品经理把PRD写成“天书”,我用AI半小时重写了一遍,他当场愣住
前端·ai编程·cursor
humcomm10 小时前
元框架的工作原理详解
前端·前端框架
canonical_entropy10 小时前
Attractor Before Harness: AI 大规模开发的方法论
前端·aigc·ai编程
zhangxingchao10 小时前
多 Agent 架构到底怎么选?从 Claude Agent Teams、Cognition/Devin 到工程落地原则
前端·人工智能·后端