ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

CASL React 权限组件实战:AbilityProvider、Can 与 useAbility 的完整指南

CASL React 权限组件实战:AbilityProvider、Can 与 useAbility 的完整指南 认证鉴权【免费下载链接】caslCASL is an isomorphic authorization JavaScript library which restricts what resources a given user is allowed to access项目地址https://gitcode.com/gh_mirrors/ca/casl点击查看免费下载casl/react是 CASL 授权体系packages/casl-ability在 React 生态中的官方集成包。它通过AbilityProvider将Ability实例注入 React Context提供声明式Can组件与命令式useAbilityHook用于在 React 与 React Native 应用中做基于规则的 UI 条件渲染与权限判断。读完本文你将掌握casl/react的安装、六大 props 与别名体系、规则的动态更新机制以及 Hook 依赖的正确写法并能在真实业务中写出可读、可维护的权限控制代码。一、包的核心能力与适用场景casl/react的设计目标是让权限判断与UI 渲染之间的胶水代码尽可能少。它对外只暴露三个核心成员见 packages/casl-react/src/index.tsAbilityProvider通过 React Context 向下分发当前Ability实例Can声明式组件负责按action subject field判断是否渲染 childrenuseAbility命令式 Hook读取 Context 中的Ability并在规则更新时触发组件重渲染。从 packages/casl-react/package.json 可以看到它的 peer 依赖casl/ability支持^4.0.0 || ^5.1.0 || ^6.0.0 || ^7.0.0React 支持^18.0.0 || ^19.0.0适用于当前主流 React 版本且官方明确说明其在 React Native 中同样可用。何时用Can何时用useAbility简单的看得见 / 看不见守卫用Can能让 JSX 保持简洁直白需要组合多个权限判断、把授权状态继续下传到更深组件树或要在事件回调、副作用里使用权限时优先useAbility。二、安装casl/react需要配合核心包casl/ability一起使用npm install casl/react casl/ability # 或 yarn add casl/react casl/ability # 或 pnpm add casl/react casl/ability三、Quick Start五分钟跑通第一个权限页面下面这段示例演示了最标准的组合先构造一个MongoAbility实例在 packages/casl-ability/src/createMongoAbility.ts 中定义底层基于 packages/casl-ability/src/Ability.ts 的Ability类再通过AbilityProvider注入组件树中混用Can与useAbilityimport { createMongoAbility } from casl/ability; import { AbilityProvider, Can, useAbility } from casl/react; const ability createMongoAbility([ { action: read, subject: Post }, { action: create, subject: Post }, ]); export function App() { return ( AbilityProvider value{ability} Can Iread aPost divList of posts/div /Can CreatePostButton / /AbilityProvider ); } function CreatePostButton() { const ability useAbility(); return ability.can(create, Post) ( buttonCreate Post/button ); }四、Can组件详解Can从AbilityProvider读取当前Ability实例在规则变化时重渲染并通过React.memo配合useMemo将规则查询结果缓存起来直到 ability、rules 或相关 props 发生变化才重新计算对应实现见 packages/casl-react/src/Can.ts。它接收 children 与 6 个属性属性作用别名do动作名如read、updateIon被检查的主体a、an、thisfield被检查的字段无not反转判断结果无权时渲染 children无passThrough无论是否允许都渲染 children配合 render function 使用无children要隐藏或渲染的元素可以是元素或 render function无4.1 字段级检查fieldfield用于字段级权限控制例如只允许用户读取某篇 post 的标题export default ({ post }) Can Iread this{post} fieldtitle Yes, you can do this! ;) /Can从源码看field会被原样传给ability.relevantRuleFor(action, subject, field)Can.ts最终在 RuleIndex.ts 的rulesFor中通过rule.matchesField(field)做字段匹配过滤。4.2 反转判断notnot会反转ability.can的结论当用户没有某项权限时才渲染 childrenexport default () Can not Icreate aPost You are not allowed to create a post /Can源码中的实现是if (props.not) isAllowed !isAllowed;Can.ts。4.3 passThrough基于 Can 定制自己的组件passThrough使Can无视ability.can的返回值始终渲染 children这对基于Can派生自定义组件非常有用。例如根据权限禁用按钮export default () ( Can Icreate aPost passThrough {({ isAllowed, reason }) ( button disabled{!isAllowed} title{reason}Save/button )} /Can )对应的渲染逻辑是return props.passThrough || isAllowed ? elements as ReactNode : null;Can.ts。4.4 children 的两种形式children 可以是 render function接收{ isAllowed, ability, reason }三个字段export default () Can Icreate aPost {({ isAllowed, reason }) ( button disabled{!isAllowed} title{reason}Create Post/button )} /Can也可以是普通 React 元素export default () Can Icreate aPost button onClick{this.createPost}Create Post/button /Can官方建议优先使用 render function 形式当用户没有对应权限时Can不会去创建额外的 React 元素从而避免无谓的渲染开销。从 Can.ts 的实现可以看到children 是函数时只会以{ isAllowed, ability, reason }为参数调用一次其中reason直接来自规则对象rule?.reason可用于向用户解释为什么被拒绝。4.5Can的行为验证packages/casl-react/spec/Can.spec.tsx 用testing-library/react覆盖了Can的全部行为契约可作为理解其内部机制的最直观证据允许时渲染 children、拒绝时不渲染renders children if ability allows...not: true时即使允许也不渲染规则更新ability.update([])后自动重渲染I、a/an/this、notprops 变化后自动重渲染props 未变化时不重复计算权限测试用jest.spyOn(ability, relevantRuleFor)验证了相同 props 重渲染只调用一次relevantRuleFor这就是React.memouseMemo的缓存效果卸载后自动取消订阅不再响应 ability 更新切换为另一个 Ability 实例后旧实例的更新不会触发本组件重渲染passThrough为 true 时永远渲染 children并把{ isAllowed: false, ability, reason: undefined }传给 render function支持用React.Fragment渲染多个子元素。五、提供 Ability 实例AbilityProvider用AbilityProvider包裹需要做权限校验的应用区域即可。它的 props 是{ value, children }实现上就是对AbilityContext.Provider的一层封装见 packages/casl-react/src/hooks/useAbility.tsimport { AbilityProvider } from casl/react; import ability from ./ability; export default function App() { return ( AbilityProvider value{ability} TodoApp / /AbilityProvider ) }注意README 早期版本使用ability{ability}当前源码v7的 prop 名是value请以value为准。Ability实例的定义方式createMongoAbility、AbilityBuilder等可参考 packages/casl-ability 与仓库中 guide/define-rules 的文档内容。在子组件里即可使用Canimport React, { Component } from react import { Can } from casl/react export class TodoApp extends Component { createTodo () { // implement logic to show new todo form }; render() { return ( Can Icreate aTodo button onClick{this.createTodo}Create Todo/button /Can ) } }六、命令式访问useAbility当组件逻辑比简单的可见性守卫更复杂时用useAbility直接拿到Ability实例。它的关键特性是当 ability 规则变化时会自动触发组件重渲染import { useAbility } from casl/react; export default () { const createTodo () { /* logic to show new todo form */ }; const ability useAbility(); return ( div {ability.can(create, Todo) button onClick{createTodo}Create Todo/button} /div ); }6.1 useAbility 的底层实现packages/casl-react/src/hooks/useAbility.ts 的实现揭示了它的工作机制通过useContext(AbilityContext)读取实例用useCallback缓存订阅函数ability?.on(updated, callback)只订阅一次用useSyncExternalStore(subscribe, getSnapshot)把 ability 的rules作为外部 store 快照接入 React 渲染周期规则变化即触发重渲染若在AbilityProvider之外使用会抛出明确错误AbilityContext is not provided. Please make sure to wrap your component tree with AbilityProvider.这里的updated事件由 RuleIndex.update 在更新规则后触发——update()先发出update事件重建规则索引再发出updated事件useAbility订阅的正是updated从而保证规则一变UI 立即刷新。6.2 useAbility 的行为验证packages/casl-react/spec/useAbility.spec.ts 验证了以下契约从 Context 中拿到的就是注入的那个Ability实例同一引用ability.update([...])后 hook 所在组件会重新执行测试断言 render 次数从 1 变为 2订阅只发生一次ability.on被调用 1 次卸载后自动取消订阅不再触发重渲染未包裹 Provider 时抛出上述错误。七、属性名与别名把 JSX 读成一句英文问句Can的设计哲学是让组件名、属性名和属性值拼在一起能读成一句英文疑问句。例如下面这行代码读作 Can I create a Postexport default () Can Icreate aPost button onClick{...}Create Post/button /Can别名对照关系源码在 Can.ts 中按of || a || an || this || on与I || do的顺序取值按类型检查时用a或anexport default () Can Iread aPost.../Can检查某个具体实例上的动作时用this替代a此时读作 Can I read this particular post?// this.props.post 是 Post 类的一个实例模型实例 export default () Can Iread this{this.props.post}.../Can如果觉得I/a的语法太花哨也可以用平实的do/on// this.props.post 是 Post 类的一个实例模型实例 export default () Can doread on{this.props.post}.../Can // 或者做字段级检查 export default () Can doread on{this.props.post} fieldtitle.../Can需要特别说明this别名与 ES class 语法中的this关键字无关它只是组件 props 的一个属性名。此外源码中还兼容一个of别名优先级最高在类型推断需要时可使用。八、TypeScript 支持casl/react本身由 TypeScript 编写所有 props 类型在 Can.ts 中通过泛型AbilityCanProps做了严格约束当 abilities 是AbilityTuple时{ do, on }、{ I, a }、{ I, an }、{ I, this }几种组合会被分别校验this只接受非SubjectType的具体实例类型a/an只接受主体类型。因此IDE 会根据你声明的Ability泛型自动提示正确的属性组合写错别名组合或传入错误类型时TypeScript 会在编译期报错children render function 的{ isAllowed, ability, reason }参数也会被完整类型化。所以不必死记所有别名——让编辑器做你的向导即可。九、更新 Ability 实例登录 / 登出场景大多数需要权限校验的应用都会有一个AuthService、LoginService或Session之类的服务负责登录登出。每当用户登录或登出就需要用新规则更新Ability实例。典型做法是在登录组件中完成这一步。假设服务端登录接口返回带角色的用户信息import { AbilityBuilder, Ability } from casl/ability; import React, { useState } from react; import { useAbility } from casl/react; function updateAbility(ability, user) { const { can, rules } new AbilityBuilder(Ability); if (user.role admin) { can(manage, all); } else { can(read, all); } ability.update(rules); } export default () { const [username, setUsername] useState(); const [password, setPassword] useState(); const ability useAbility(); const login () { const params { method: POST, body: JSON.stringify({ username, password }) }; return fetch(path/to/api/login, params) .then(response response.json()) .then(({ user }) updateAbility(ability, user)); }; return ( form {/* input fields */} button onClick{login}Login/button /form ); };这条链路的核心是ability.update(rules)AbilityBuilder见 packages/casl-ability/src/AbilityBuilder.ts负责生成新规则数组update()在 RuleIndex.ts 中重建内部规则索引并广播updated事件从而触发所有Can与useAbility消费者重渲染。这也解释了为什么登录后页面权限立即变化——不是手动刷新组件而是规则变化自动驱动的。十、在 Hook 依赖中使用 useAbility 的返回值一个容易踩的坑把useAbility()返回的ability直接放进自定义 hook 的依赖数组不会在规则更新时触发重渲染。原因在于 ability 对象本身的引用在update()前后是不变的变化的只是它的rules。正确做法是把ability.rules作为依赖const posts React.useMemo(() getPosts(ability), [ability.rules]); // ✅ 调用 ability.update 会更新 posts 列表这一建议与useAbility的实现一致getSnapshot返回的就是ability.rules的引用useAbility.tsReact 据此判定快照是否变化。因此只要把ability.rules放进依赖规则更新就能精确触发重新计算。十一、总结casl/react用三个简洁的 API 覆盖了 React 应用权限控制的全部常见需求AbilityProvider负责分发实例Can负责声明式条件渲染useAbility负责命令式访问与规则驱动的重渲染。配合 CASL 的规则索引与事件机制RuleIndex.update触发updated事件权限规则一变整个组件树的守卫立即同步刷新。在实际项目中建议遵循简单守卫用Can、复杂逻辑用useAbility、Hook 依赖写ability.rules的实践准则即可获得既清晰又高性能的权限 UI。延伸阅读核心库 packages/casl-abilityAbility、AbilityBuilder、createMongoAbility与规则索引实现packages/casl-react/src/Can.tsCan完整实现props 别名、缓存、render functionpackages/casl-react/src/hooks/useAbility.tsContext 与useSyncExternalStore的实现细节测试证据packages/casl-react/spec/Can.spec.tsx 与 packages/casl-react/spec/useAbility.spec.ts规则定义指南docs-src/src/content/pages/guide/define-rules/en.md。赞分享认证鉴权【免费下载链接】caslCASL is an isomorphic authorization JavaScript library which restricts what resources a given user is allowed to access项目地址https://gitcode.com/gh_mirrors/ca/casl点击查看免费下载相关推荐CASL React 集成指南在 React 应用中使用 casl/react 的 AbilityProvider、Can 组件与 useAbility HookCASL React 集成指南在 React 应用中使用 casl/react 的 AbilityProvider 、 Can 组件与 useAbility认证鉴权RabbitMQ 3.10.22 维护版本解析Erlang 版本要求与 RPM 打包修复实战指南RabbitMQ 3.10.22 维护版本解析Erlang 版本要求与 RPM 打包修复实战指南 RabbitMQ 3.10.22 是 3.10.x 发布系列认证鉴权在 Vue 3 应用中集成 CASL 权限控制casl/vue 插件、Can 组件与响应式 Ability 完整指南在 Vue 3 应用中集成 CASL 权限控制casl/vue 插件、Can 组件与响应式 Ability 完整指南 CASLisomorphic aut认证鉴权上一篇React Masonry Component 常见问题解决方案下一篇OpenComponents 项目常见问题解决方案创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表