GraphQL 后端上的 RBAC 使用 CASL 和 graphql-shield 并与我的 React 前端共享规则
RBAC on GraphQL backend using CASL and graphql-shield and sharing rules with my React front-end
我是 运行 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,并且权限检查逻辑完全隐藏在后端
我是 运行 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,并且权限检查逻辑完全隐藏在后端