Graphql @include 带表达式

2024-03-15

我正在实现一个查询,该查询应该根据用户登录状态提供响应中的某些字段。

具体来说,我想得到“点率”仅当字段$authenticationToken已通过并且希望避免通过$authenticated在下面的查询中。我想避免发送的原因$authenticated客户端发送时是否会犯错误$authenticated = true but $authenticationToken = null.

query ToDoQuery($authenticationToken: String, $authenticated: Boolean!) {
    pointRate(accessToken: $authenticationToken) @include(if: $authenticated) {
        status
    }
}

所以,实际上你想这样做

i) 如果 $authenticationToken 被传递,你想要得到“pointRate”。

ii) 并且您还希望避免在后续中传递 $authenticated 查询。因为您关心您的客户是否可以做出一些贡献 诸如发送经过身份验证的错误是正确的,其中身份验证令牌 为空。

因此,总的来说,我想回答的是,如果您想使用 GraphQL 自己处理身份验证,首先您必须创建一个令牌,然后您必须在每个请求或后续请求中传递该令牌。否则是不可能的。因为未经身份验证,不会提供敏感数据。

另一方面,您可以使用会话身份验证。您可以访问所有数据,直到会话关闭。

如果不满意,您可以阅读以下简短描述以及与您类似的场景。我还尝试积累一些相关的示例解决方案以便更好地理解,它可能会让您更加清晰。

由于GraphQL API是完全公开的,您可以通过两种方式进行身份验证。

  1. 让 Web 服务器(例如 Express 或 nginx)负责身份验证。
  2. 在 GraphQL 本身中处理身份验证。

If you 在网络服务器中进行身份验证,您可以使用标准身份验证包(例如,express 的passport.js),并且许多现有的身份验证方法将开箱即用。您还可以根据自己的喜好添加和删除方法,而无需修改 GraphQL 架构。

If 您正在自己实施身份验证,执行以下操作

  • 确保永远不要以明文、MD5 或 SHA-256 形式存储密码 散列

    • 使用 bcrypt 之类的东西

    • 确保不要将会话令牌按原样存储在服务器上,您 应该先对它们进行哈希处理

    • 您可以编写一个登录方法来设置上下文。自从突变 是一个接一个执行的,而不是并行执行的,你可以确定 上下文是在登录突变后设置的:

      mutation { loginWithToken(token: "6e37a03e-9ee4-42fd-912d-3f67d2d0d852"), do_stuff(greeting: "Hello", name: "Tom"), do_more_stuff(submarine_color: "Yellow") }

    • 我们没有通过标头或查询参数(如 JWT、OAuth 等)传递令牌,而是将其作为 GraphQL 查询的一部分。您的架构代码可以使用 JWT 库本身或其他工具直接解析令牌。

    • 请记住在传递敏感信息时始终使用 HTTPS :)

因为并行执行对于性能来说很重要。突变和查询按照给定的顺序连续执行。 因此,在大多数情况下,最好在 Web 服务器中处理身份验证。它不仅更通用,而且更灵活。


设想:

首先通过以下内容

import jwt from'express-jwt';
import graphqlHTTP from'express-graphql';
import express from'express';
import schema from'./mySchema';
const app = express();

app.use('/graphql', jwt({
  secret: 'shhhhhhared-secret',
  requestProperty: 'auth',
  credentialsRequired: false,
}));
app.use('/graphql', function(req, res, done) {
  const user = db.User.get(req.auth.sub);
  req.context = {
    user: user,
  }
  done();
});
app.use('/graphql', graphqlHTTP(req => ({
    schema: schema,
    context: req.context,
  })
));

如果你检查上面的部分,你会发现 API 根本不安全。它可能会尝试验证 JWT,但如果 JWT 不存在或无效,请求仍将通过(请参阅credentialsRequired: false)。为什么?我们必须允许请求通过,因为如果我们阻止它,我们就会阻止整个 API。这意味着,我们的用户甚至无法调用 loginUser 突变来获取令牌来验证自己的身份。


解决方案#1:

使用身份验证解析器而不是端点的准系统示例。

import { GraphQLSchema } from 'graphql';
import { Registry } from 'graphql-helpers';

// The registry wraps graphql-js and is more concise
const registry = new Registry();

registry.createType(`
  type User {
    id: ID!
    username: String!
  }
`;

registry.createType(`
  type Query {
    me: User
  }
`, {
  me: (parent, args, context, info) => {
    if (context.user) {
      return context.user;
    }
    throw new Error('User is not logged in (or authenticated).');
  },
};

const schema = new GraphQLSchema({
  query: registry.getType('Query'),
});

当请求到达我们的 Query.me 解析器时,服务器中间件已经尝试对用户进行身份验证并从数据库中获取用户对象。在我们的解析器中,我们可以检查用户的 graphql 上下文(我们在 server.js 文件中设置上下文),如果存在则返回它,否则抛出错误。

注意:您可以轻松返回 null 而不是抛出错误,我实际上会推荐它。

解决方案#2:

使用express-graphql的功能组合(基于中间件)

import { GraphQLSchema } from 'graphql';
import { Registry } from 'graphql-helpers';
// See an implementation of compose https://gist.github.com/mlp5ab/f5cdee0fe7d5ed4e6a2be348b81eac12
import { compose } from './compose';

const registry = new Registry();

/**
* The authenticated function checks for a user and calls the next function in the composition if
* one exists. If no user exists in the context then an error is thrown.
*/
const authenticated =
  (fn: GraphQLFieldResolver) =>
  (parent, args, context, info) => {
    if (context.user) {
      return fn(parent, args, context, info);
    }
    throw new Error('User is not authenticated');
  };

/*
* getLoggedInUser returns the logged in user from the context.
*/
const getLoggedInUser = (parent, args, context, info) => context.user;

registry.createType(`
  type User {
    id: ID!
    username: String!
  }
`;

registry.createType(`
  type Query {
    me: User
  }
`, {
  me: compose(authenticated)(getLoggedInUser)
};

const schema = new GraphQLSchema({
  query: registry.getType('Query'),
});

上面的代码与第一个代码片段的工作方式完全相同。我们没有在主解析器函数中检查用户,而是创建了一个高度可重用和可测试的中间件函数来实现相同的功能。这种设计的直接影响可能还不明显,但想一想如果我们想添加另一个受保护的路由并记录解析器的运行时间会发生什么。我们的新设计非常简单:

const traceResolve =
  (fn: GraphQLFieldResolver) =>
  async (obj: any, args: any, context: any, info: any) => {
    const start = new Date().getTime();
    const result = await fn(obj, args, context, info);
    const end = new Date().getTime();
    console.log(`Resolver took ${end - start} ms`);
    return result;
  };

registry.createType(`
  type Query {
    me: User
    otherSecretData: SecretData
  }
`, {
  me: compose(traceResolve, authenticated)(getLoggedInUser)
  otherSecretData: compose(traceResolve, authenticated)(getSecretData)
};

使用此技术将帮助您构建更强大的 GraphQL API。函数组合是身份验证任务的一个很好的解决方案,但您也可以使用它来记录解析器、清理输入、调整输出等等。

解决方案#3:

一个不错的解决方案是将数据提取分解到一个单独的层中并在那里进行授权检查。 下面是遵循上述原则的示例。它用于获取用户可以看到的所有待办事项列表的查询。

对于以下查询,

{
  allLists {
    name
  }
}

不要这样做:

//in schema.js (just the essential bits)
allLists: {
  resolve: (root, _, ctx) => {
    return sql.raw("SELECT * FROM lists WHERE owner_id is NULL or owner_id = %s", ctx.user_id);
  }
}

相反,我建议你这样做:

// in schema.js (just the essential bits)
allLists: {
  resolve: (root, _, ctx) => {
    //factor out data fetching
    return DB.Lists.all(ctx.user_id)
      .then( lists => {
        //enforce auth on each node
        return lists.map(auth.List.enforce_read_perm(ctx.user_id) );
      });
  }
}
//in DB.js 
export const DB = {
  Lists: {
    all: (user_id) => {
      return sql.raw("SELECT id FROM lists WHERE owner_id is NULL or owner_id = %s, user_id);
    }
  }
}
//in auth.js
export const auth = {
  List: {
   enforce_read_perm: (user_id) => {
     return (list) => {
       if(list.owner_id !== null && list.owner_id !== user_id){
         throw new Error("User not authorized to read list");
       } else {
         return list;
       }
     }
   }
}

您可能认为 DB.Lists.all 函数已经在强制执行权限,但在我看来,它只是试图不获取太多数据,权限本身并不是在每个节点上单独强制执行的。这样,您就可以在一个地方进行身份验证检查,并且可以确保它们将被一致地应用,即使您在许多不同的地方获取数据也是如此。

解决方案#4:

身份验证流程可以通过多种不同方式完成。

i) basic auth, 
ii) session auth, or
iii) token auth.

由于您的问题是根据令牌身份验证提出的,因此我想与您见面使用令牌身份验证的 Scaphold。我们所做的一切,无论是将用户登录到 Scaphold 还是将您的用户登录到您的应用程序,我们都使用令牌来管理用户的身份验证状态。身份验证流程的工作原理如下:

a) 用户使用用户名和密码登录。

b) GraphQL 服务器根据用户的散列密码验证数据库中的用户。

c) 如果成功,服务器将返回一个 JSON Web 令牌 (JWT),它是带有到期日期的 Base64 编码令牌。这是身份验证令牌。

d) 要使用身份验证令牌,您将来的请求应在标头中包含身份验证令牌,如下所示

{ 授权:'承载' + [Auth_Token] }

现在,每次服务器(可能是 Node Express)看到标头中的令牌时,它都会解析出令牌并验证它,并在 GraphQL 世界中将识别出的用户保存在上下文中,以便在应用程序的其余部分中使用。用户现已登录。

有关更多信息,您可以在本教程中了解有关 @include 的更多信息:https://github.com/mugli/learning-graphql/blob/master/4.%20Querying%20with%20Directives.md#include https://github.com/mugli/learning-graphql/blob/master/4.%20Querying%20with%20Directives.md#include

要逐步学习 graphql 身份验证,您可以阅读本教程:GraphQL 身份验证 https://www.howtographql.com/graphql-js/5-authentication/

资源链接:

  1. 身份验证用 GraphQL https://scaphold.io/community/blog/authentication-in-graphql/
  2. 身份验证指南 GraphQL https://dev-blog.apollodata.com/a-guide-to-authentication-in-graphql-e002a4039d1
  3. GraphQL 的最佳实践 安全 https://scaphold.io/community/questions/graphql-security-best-practices/
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Graphql @include 带表达式 的相关文章

  • graphql 中的 Console.log 解析器数据

    我正在尝试在 graphql 应用程序中 console log 解析器 graphiql 部分可以工作 我可以启动服务器并查看 graphql 仪表板 然后使用根查询在浏览器窗格中检索结果 但我无法在浏览器控制台中 console log
  • 登录方式使用GINA定制

    我知道在 GINA 中找到大师并不容易 但我的问题最接近进程间通信 IPC 我用非托管 c 编写了我的自定义 GINA 我在其中包含了一个方法来检查用户尝试的指纹的有效性为了登录 该函数将调用正在运行的系统Windows服务中用C 编写的一
  • 如何授予用户访问 SQL Server 中的 sys.master_files 的权限?

    我需要授予数据库用户读取权限sys master files桌子 我怎样才能做到这一点 目前用户拥有以下权限 Calling SELECT on sys master files返回空结果 我还使用以下命令测试了相同的查询sa用户按预期工作
  • 尝试使用 SQL 身份验证登录失败

    我正在尝试使用 sa 用户名及其密码连接到 SQL Server 2008 在 SQL Server 日志文件中我看到以下错误 用户 sa 登录失败 原因 尝试使用 SQL 登录 认证失败 服务器配置为 Windows 身份验证 仅有的 当
  • 在浏览器上验证 JWT

    我一直在读关于JWT https jwt io 我知道它分为三个部分 即header payload and signature 我将哈希算法保留在标头中 将基本信息保留在有效负载中例如 姓名 年龄 职务 有效期等在有效负载中 然后这两个都
  • 权限被拒绝,但在清单中设置了权限

    我收到此错误 删除失败 ENOENT 没有此类文件或目录 storage emulated 0 screenShot jpgFileNotFoundException storage emulated 0 screenShot jpg 打开
  • 使用 graphql 从 github 提交统计数据

    有人可以告诉我 哪里有办法使用 graphql api 获取特定存储库的所有提交和统计信息 现在我以这样的查询结束 query viewer repository name CRM system ref qualifiedName mast
  • Magento Connect Manager 和 ftp 写入文件权限

    将我的网站从开发域转移到实时域后 我尝试使用 magento 连接管理器安装扩展 但是我不断收到 CONNECT ERROR Please check for sufficient ftp write file permissions Yo
  • nginx代理认证拦截

    我有几个服务 它们支持 nginx 实例 为了处理身份验证 在 nginx 中 我拦截每个请求并将其发送到身份验证服务 在那里 如果凭据正确 我将设置一个包含用户相关信息的 cookie 现在 请求应该被路由到适当的服务 并设置 cooki
  • 401 Unauthorized("detail":"未提供身份验证凭据。")

    我在后端使用 djoser 的身份验证 当我通过具有内容类型和授权标头的邮递员在 account me 发出获取请求时 我得到了正确的响应 但是当我尝试从我的角度客户端执行相同的请求时 我得到401 Unauthorized detail
  • 如何检索分配给 Django 中的组的所有权限

    我正在执行一项任务来检索分配给 Django 中的组的一组权限 我可以使用以下代码获取创建的组 但无法使用它来获取分配给它们的权限 from django contrib auth models import Group Permissio
  • 将密钥对添加到现有 EC2 实例

    我被给予AWS控制台访问正在运行 2 个实例的帐户 但我无法关闭 在生产中 但是 我想获得对这些实例的 SSH 访问权限 是否可以创建一个新的密钥对并将其应用到实例 以便我可以通过 SSH 访问 获取现有的pem当前无法选择创建实例所用的密
  • 将 Chrome 扩展上的权限移至可选

    我了解到 Chrome 在添加新权限时会禁用扩展程序 当我向扩展添加新功能时 我需要在permissions列表 现在我知道我应该使用optional permissions 我的问题是 如果我将网站列表从permissions to op
  • AADSTS50011:请求中指定的回复 URL 与为应用程序配置的回复 URL 不匹配:'*****-*****-*****-*****-**** ******'

    我正在尝试使用 Django Microsoft 身份验证后端 允许 o365 登录我的 Django 项目 https django microsoft auth readthedocs io en latest https django
  • 目前最流行的 Ruby on Rails AUTHORIZATION gem/plugin 是什么? [关闭]

    就目前情况而言 这个问题不太适合我们的问答形式 我们希望答案得到事实 参考资料或专业知识的支持 但这个问题可能会引发辩论 争论 民意调查或扩展讨论 如果您觉得这个问题可以改进并可能重新开放 访问帮助中心 help reopen questi
  • Android:GoogleIdTokenVerifier.Builder 中的 Transport 和 jsonFactory 是什么?

    在下面的代码中 什么是transport and jsonFactory 我不明白 https developers google com identity sign in android backend auth using a goog
  • WCF WebHttp 混合身份验证(基本和匿名)

    所有这些都与 WebHttp 绑定有关 托管在自定义服务主机中 IIS 目前不是一个选项 我已经实现了自定义 UserNamePasswordValidator 和自定义 IAuthorizationPolicy 当我将端点的绑定配置为使用
  • 游戏中心邀请处理程序,它属于哪里?

    我已经搜索了该网站并发现了这个 GameCenter 邀请处理程序 https stackoverflow com questions 4639284 gamecenter invitation handler He says 正如文档中所
  • 不显示 WRITE_EXTERNAL_STORAGE 的权限对话框

    I want to download a file using DownloadManager And DownloadManager wants to WRITE EXTERNAL STORAGE permission I have in
  • 如何使用 Google 帐户对我们网站中的用户进行身份验证

    如何在我们的网站中使用 Google 帐户对用户进行身份验证 我希望用户重定向到谷歌登录页面 然后将他重定向到我的网站 我想要这个 PHP 实现 你要OAuth http code google com apis accounts docs

随机推荐