【问题标题】:Lists and pagination authorization in GraphQL business layerGraphQL 业务层中的列表和分页授权
【发布时间】:2018-02-09 00:39:40
【问题描述】:

在 Dan Schafer 的出色 "GraphQL at Facebook" talk from React Europe 中,他讨论了如何在业务层模型中集中授权以避免必须为通向授权节点的每条边复制授权逻辑的问题。

这适用于Todo.getById(1) 之类的东西,在我的情况下,它最终会在数据库中查询SELECT * from todos WHERE id=1,然后使用checkCanSee(resultFromDatabase) 验证授权。

但是,假设我的 todos 表现在包含来自多个用户的 100,000 个待办事项,纯粹在业务层执行授权变得不切实际,因为我需要获取每个待办事项,使用共享授权逻辑过滤结果然后切片执行分页。

我认为解决这个问题的唯一方法是让授权逻辑驻留在持久层本身是不是错了?

【问题讨论】:

    标签: pagination authorization graphql


    【解决方案1】:

    我认为 Dan 演讲的要点之一是使用 GraphQL 处理授权的方式与典型的 REST 端点不同。

    在 REST 中,每个资源通常与单个端点相关联。当向该端点发出请求时,在处理请求之前检查请求者是否被授权是有意义的。使用 GraphQL,我们可能会在同一个请求中获取多个资源,因此不再需要这种行为。正如丹所说:

    如果您看不到 [请求的资源] 之一,我们不希望完全破坏请求。

    因此,GraphQL 的首选方法是实现某种 per-node 授权机制,并且只返回请求者有权查看的资源。这正是谈话节目中的例子——一种方法。

    如果您将待办事项存储在 SQL 数据库表中,那么您的代码只需进行 SELECT * from todos WHERE creator_id=${viewer.id} 之类的查询并完全省略使用 checkCanSee 之类的函数即可。

    同样,您可以使用限制偏移、游标等将分页直接烘焙到您的查询中。是的,既然您现在让您的数据库完成繁重的工作,您可以说我们已经进入了持久层.但是,接收请求、清理输入、构建适当的查询并以 GraphQL 可以使用的形式返回结果仍然取决于您的业务逻辑。

    我不能代表 Dan,但我想他的意图并不是暗示这是实现节点授权的唯一(甚至是最佳)方式。我认为更重要的一点是,例如,如果您正在获取:

    {
      header
      todos  {
        description
      }
      quoteOfTheDay
    }
    

    即使是未经授权的客户端仍应从服务器获得响应,然后它可以使用该响应为最终用户呈现页面(即使该响应包含一个空的待办事项数组)。

    【讨论】:

    • 我仍然相信让授权逻辑驻留在存储层是次优的。例如,当授权在缓存后完成时,您将如何在业务逻辑和存储层之间实现缓存?此外,如果我决定对某些字段使用 SQL 连接器,而对其他字段使用另一个后端系统,该怎么办。我必须对两个后备系统实施授权。
    • GraphQL 的目的是拥有一个“单一的事实来源”。这意味着您想要做的事情并没有真正明确地被允许。您必须让所有后端都使用 GraphQL 来维护设计模式。
    【解决方案2】:

    您可以根据授权结果进行查询。在您的 Todo 示例中:

    1. 询问授权服务器允许您查看其待办事项,
    2. SELECT * FROM todos WHERE owner IN [<permitted owners]]

    【讨论】:

      【解决方案3】:

      GraphQL 并未确切建议您应该如何使用它。对于 Facebook,您必须了解他们尊重许多政策,因此他们决定将 GraphQL 保持在业务层之上(否则仍然有意义)。为了解决您提到的问题,他们实现了缓存和异步处理。这样,他们会整理和缓存您的数据请求,从而更轻松地检索以前查询的项目。

      在您的情况下,如果您不需要业务层,您可以决定将 GraphQL 添加到存储层之上。

      GraphQL 应该被视为 API 的一种设计模式。确保数据/实体模型有一个数据检索标准(又称单一事实来源)以及使大型系统的数据身份验证和 API 集成过程更容易,这很有帮助。

      【讨论】:

        【解决方案4】:

        在又一轮寻找答案之后,我偶然发现了一些来自 Lee Byron 的有趣的 cmets,它们对这个主题有所启发:

        存储层不应处理授权,但应公开限制数量的 API 正在返回的数据。

        在上述处理数十万个待办事项的场景中,这可以通过暴露一些东西来实现 就像getTodosByUserId 一样,它返回所有属于特定用户的待办事项。业务逻辑层 然后将通过过滤切片结果来处理授权和分页。但是如果 一个用户有数千个待办事项?可能会向存储层添加另一个过滤器选项 比如getTodosByUserId({ userId: 1, completed: false })

        Lee 的“让我们谈谈缓存”评论的一个重要结论是结果 来自存储层的应该可以很容易地缓存在 Redis 或 memcached 之类的东西中。抓取 来自 Redis 的一千个待办事项,然后在您的业务逻辑层中过滤和切片它们可能是 比为每个请求都查询 SQL 数据库要便宜得多。

        【讨论】:

        • 我这样说:尽管很多人喜欢单一职责原则,但假装 API 设计和授权逻辑实际上是独立的是不切实际的。例如,如果您的普通用户只被允许查看自己的待办事项,那么使用全局todos 节点就没有意义。它应该是viewer { todos { ... } }。所以授权不是你的 API 的驱动程序。它只是一种通常不应该失败的安全措施(就像异常是针对例外情况而不是针对典型的执行流程)。全局todos 节点只会检查一次isAdmin
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2017-05-03
        • 2010-11-09
        • 2021-08-19
        • 2017-09-28
        • 2012-11-18
        • 2018-04-29
        • 2011-06-29
        相关资源
        最近更新 更多