在Dan Schafer的优秀"GraphQL at Facebook" talk from React Europe中,他讨论了如何在业务层模型中集中授权,避免了必须为通向授权节点的每个边缘复制授权逻辑的问题。
这适用于类似Todo.getById(1)
的内容,在我的情况下最终会查询SELECT * from todos WHERE id=1
的数据库,然后使用checkCanSee(resultFromDatabase)
验证授权。
但是,让我们说我的todos
表现在包含来自多个用户的100,000个待办事项,纯粹在业务层执行授权变得不切实际,因为我需要获取每个待办事项,过滤结果使用共享授权逻辑,然后对其进行切片以执行分页。
我错误地认为解决这个问题的唯一方法是让授权逻辑驻留在持久层本身吗?
答案 0 :(得分:1)
我认为Dan的演讲之一就是与GraphQL处理授权的方式不同,而不是典型的REST端点。
在REST中,每个资源通常与单个端点相关联。当向该端点发出请求时,在处理请求之前检查请求者是否被授权是有意义的。使用GraphQL,我们可能在同一请求中获取多个资源,因此不再需要此行为。正如丹所说:
如果您看不到[所请求的资源]之一,我们不想完全放弃请求。
因此,使用GraphQL的首选方法是实现某种 每节点 机制以进行授权,并且只返回请求者有权查看的资源。而这正是谈话中的例子 - 一种方法。
如果将待办事项存储在SQL数据库表中,那么您的代码只需进行SELECT * from todos WHERE creator_id=${viewer.id}
之类的查询就可以完全有意义,并且完全不使用像checkCanSee
这样的函数。
同样地,你可以使用限制偏移,游标等将你的分页烘焙到你的查询中。是的,既然你现在让你的数据库执行繁重的工作,你可以说我们已经进入了持久层。但是,仍然可以根据业务逻辑来获取请求,清理输入,构造适当的查询并以GraphQL可以使用的形式返回结果。
我不能代表丹,但我想他的意图并不是暗示这是实现节点授权的唯一(甚至是最佳)方式。我认为更重要的一点是,如果你是,例如,提取:
{
header
todos {
description
}
quoteOfTheDay
}
即使是未经授权的客户端仍然应该从服务器获得响应,然后它可以用来为最终用户呈现页面(即使该响应包含一个空数组的待办事项)。
答案 1 :(得分:0)
您可以根据授权结果进行查询。在您的Todo示例中:
SELECT * FROM todos WHERE owner IN [<permitted owners]]
答案 2 :(得分:0)
GraphQL并不完全建议您应该如何使用它。对于Facebook,您必须了解他们尊重许多政策,因此他们决定将GraphQL保留在业务层之上(否则它仍然有意义)。为了解决您提到的问题,他们实现了缓存和异步处理。通过这种方式,他们可以整理和缓存您的数据请求,从而更轻松地检索以前查询过的项目。
在您的情况下,如果您不需要业务层,则可以决定在存储层上方添加GraphQL。
应将GraphQL视为API的设计模式。确保数据/实体模型的数据检索标准(即单一事实来源)以及使大型系统更容易进行数据认证和API集成过程是有帮助的。
答案 3 :(得分:0)
经过另一轮寻找答案后,我偶然发现了Lee Byron的一些有趣的评论,这些评论为这个问题提供了一些启示:
存储层不应该处理授权,但它应该公开限制数量的API 正在返回的数据。
在处理成千上万个待办事项的上述场景中,这可以通过暴露某些东西来实现
比如getTodosByUserId
,它返回所有属于特定用户的待办事项。业务逻辑层
然后通过过滤切片结果来处理授权和分页。但是如果
用户有成千上万的待办事项?可能会向存储层添加另一个过滤器选项
例如getTodosByUserId({ userId: 1, completed: false })
。
Lee的评论是“让我们谈谈缓存”的一个重要内容就是结果 从存储层应该可以轻松地安装在像Redis或memcached这样的东西上。取 来自Redis的一千个待办事项,然后在您的业务逻辑层中过滤和切片它们可能是 比每次请求查询SQL数据库要便宜得多。