问题描述
我正在运行 Mongoose 并使用 GraphQL (Apollo) 公开 API。
我想实现一个 RBAC,经过一些研究,我提出了一个使用 CASL 和 graphql-shield 的解决方案。 理想情况下,我希望与我的 React 前端共享规则。
第一步,在一张纸上计划。
我将首先定义我的操作:创建、读取、更新、删除。
然后我会定义我的主题:汽车、摩托车。
完成后,我将继续定义我的角色:CarSpecialist、MotoSpecialist、Admin。 然后我会定义一些条件:“主题是我自己的”等。
最后,我会为每个角色分配一组能力(动作、主题、条件的组合)。
现在完成所有这些工作后,我开始实际编写我的解决方案。
我首先在 CASL 中编写能力:动作和主题非常容易定义。
条件有点棘手,我至少有两个选择:
-
我使用“模糊”的概念,而这些概念又必须由任何需要强制执行它们的人(后端或前端)来解释。
-
我使用 CASL mongoose 集成插件,但代价是无法与我的前端共享。
有什么可以选择的输入吗?
现在一旦定义了 CASL 能力,是否由 graphql-shield 来执行它们?
如何将 (CASL) 操作、主题和条件映射到 graphql 术语:架构、查询、突变......?
解决方法
我会尽量回答这个问题:
- 如果使用默认条件,您不会失去与 UI 共享权限的能力。当您运行
ability.can
时,条件在 js 中被解释。因此,如果 mongo 查询语言适合您,那么无需更改它! - Graphql shield 是一种特殊的 graphql 中间件。如果您使用 casl 和 graphql 中间件,您不需要 graphql shield! 使用 casl + 自定义 graphql 中间件或 graphql-shield
- 每个 graphql 类型都有底层源类型。源类型基本上是您的域模型或只是封装业务逻辑的数据库模型。这是您的映射 :) 只需检查源类型的权限即可。但是,如果您与 UI 共享权限,那么您需要将为源类型编写的后端权限(在发送到 UI 之前)转换为可以应用于 graphql 类型的权限!或者,您可以将一些私有属性(例如 Car 的
ownerId
)公开为 graphql 类型的一部分。但如果这样做的唯一目的是满足权限共享,那么我会选择转换选项:
function defineAbility(user,props) {
const { can,rules} = new AbilityBuilder(Ability)
can('read','Post',{ [props.authorId]: user.id })
// ...
return rules;
}
const currentUser = { id: 1 }
const backendRules = defineAbility(currentUser,{
authorId: 'authorId'
});
const uiRules = defineAbility(currentUser,{
authorId: 'author.id'
});
或者,您可以通过在每个 graphql 类型上公开子类型来检查后端的权限并与前端共享结果:
{
cars {
items {
permissions {
canUpdate
canRead
}
}
}
}
这样做的后果是您的服务器将花费更多时间来生成响应,尤其是在您检索用于分页的项目时。因此,在继续之前检查响应时间。这样做的好处是你不需要在 UI 上使用 casl,并且权限检查逻辑完全隐藏在后端