GraphQL 授权层的复杂性及 Hasura 的解决方案

作者:API传播员 · 2025-09-12 · 阅读时间:5分钟

构建授权层是GraphQL API 编写授权逻辑更加复杂。在本文中,我们将探讨如何为自定义 GraphQL 服务器构建授权逻辑,并分析其复杂性来源。同时,我们还会比较 Hasura 的解决方案,了解其如何通过声明式方法和谓词下推技术简化授权逻辑的实现。


数据建模

数据建模是构建授权系统的基础。它定义了数据类型及其关系,从而决定了授权系统的设计方式。

例如,一个应用允许用户查看所有用户的公共数据以及他们的私人数据。此时,数据模型需要包含相关的表列和与私人数据相关的关系。


角色和属性

授权系统的建模通常采用两种方法:

  • RBAC(基于角色的访问控制:通过定义角色及其权限来控制访问。
  • ABAC(基于属性的访问控制):根据用户属性(如用户类型、资源类型、访问环境等)来决定权限。

通过定义角色和属性,可以为应用程序设计灵活的授权规则。


嵌套规则

GraphQL 查询可能涉及多个嵌套字段,这些字段可能跨越多个数据源。授权逻辑需要在上下文中动态应用到所有嵌套级别。

例如,以下查询中,授权规则需要同时适用于用户和订单:

query {
  用户 {
    id
    名称
    订单 {
      id
      order_total
    }
  }
}

这种嵌套规则的实现增加了授权逻辑的复杂性。


性能优化与谓词下推

授权检查不应显著增加响应延迟。然而,在自定义 GraphQL 服务器中,授权检查通常在数据获取之后进行,这可能导致额外的数据处理,降低性能。

谓词下推 是一种优化方法,它将授权规则直接应用到数据库查询中。例如,在电子商务应用中,仅查询当前用户的订单数据。这种方法不仅更高效,还能提升安全性


为什么构建授权层很复杂?

在自定义 GraphQL 服务器中,授权逻辑的实现方式多种多样,包括:

  • API 范围的授权:在请求级别应用规则。
  • 基于解析器的授权:在解析器内实现授权逻辑。
  • 基于模式/模型的授权:授权逻辑依赖于 GraphQL 模式。

如果每个字段都需要授权规则,基于解析器的授权会导致大量样板代码,难以维护。而基于模式的授权则更灵活,但也需要额外的设计和实现。


使用上下文构建授权逻辑

GraphQL 中的授权通常通过 Context 对象实现。上下文对象在每次请求时创建,并传递给每个解析器,包含用户身份验证和授权信息。

解析和验证 JWT 令牌

JWT(JSON Web Token)是常见的身份验证方式。以下是解析和验证 JWT 令牌的示例代码:

try {
  if (token) {
    return jwt.verify(token, YOUR_SECRET_KEY);
  }
  return null;
} catch (err) {
  return null;
}

传递上下文

在解析器中,授权逻辑可以通过上下文对象实现。例如:

users: (parent, args, contextValue) => {
  if (!contextValue.user) return null;
  return ['bob', 'jake'];
};

基于 GraphQL 模式的授权

对于复杂的授权需求,可以基于 GraphQL 模式定义规则。例如,允许用户仅访问自己的数据:

const CanPublishPost = preExecRule(async (context, fieldArgs) => {
  const graphQLResult = await graphql({
    schema: context.schema,
    source: `query post($postId: ID!) {
      post(id: $postId) {
        author {
          id
        }
      }
    }`,
    variableValues: { postId: fieldArgs.postId },
  });
  const post = graphQLResult.data?.post;
  return post && post.author.id === context.user?.id;
});

Hasura 的声明式授权解决方案

Hasura 提供了一个强大的授权引擎,通过声明式方法简化授权逻辑的实现。

声明式授权

Hasura 允许开发者通过元数据配置声明角色及其权限。这种方法更易于创建、维护和审计。

细粒度访问控制

Hasura 支持基于角色的访问控制,权限规则可应用于所有 CRUD 操作。每个角色都会生成一个特定的 GraphQL 模式,表示该角色可访问的查询和字段。

授权规则可以基于以下条件:

  • 数据关系属性
  • 用户属性
  • 数据模型的部分屏蔽或标记

谓词下推

Hasura 自动将授权规则下推到数据库查询中,从而避免额外的数据处理,提高性能。


跨源授权

Hasura 支持跨数据源的授权规则整合。通过将解析后的值作为标头转发到外部服务,可以轻松在外部服务中应用授权规则。


总结

构建 GraphQL API 的授权层是一项复杂的任务,涉及数据建模、角色定义、嵌套规则、性能优化等多个方面。Hasura 提供了一种声明式的解决方案,通过细粒度访问控制和谓词下推技术,显著简化了授权逻辑的实现。对于开发者而言,Hasura 是一种高效且安全的选择。

原文链接: https://hasura.io/blog/the-complexity-of-building-a-graphql-api-permissions-layer-and-how-hasura-solves-this