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 带表达式 的相关文章

  • 在 keycloak 令牌中使用“sub”声明作为内部数据库中的用户 ID 是否安全

    我正在开发一个将来可能支持社交登录的应用程序 除了我们自己的 keycloak 用户名 电子邮件注册之外 我正在尝试确定使用 keycloak ID 令牌中的 sub 声明作为我们内部 mongo 数据库中用户的主键 id 字段 是否安全
  • 数据库文件和 ASP.NET 登录控件

    我正在尝试 ASP NET 登录控制教程 一切正常 但是 我不知道如何让登录控件使用我自己的数据库 SQL Server 2005 而不是使用它的 mdf 文件 我也不知道这个文件是从哪里创建的 因为它根本没有出现在我的解决方案中 如果我能
  • 如何在 GWT 中实现登录屏幕?

    我正在为后端应用程序编写一个小型 GWT 前端 我想知道 GWT 应用程序的最佳安全模型是什么 我正在考虑实现一种 RPC 方法 该方法从客户端网页接收用户密码的 MD5 然后将会话 ID 传回客户端页面 或失败代码 所有后续调用都将简单地
  • PermissionError:[Errno 13]权限被拒绝:尽管在AWS EC2实例上正确运行,但无法在浏览器上打开Jupyter

    我已经在AWS ubuntu eu central 1b 上设置了一个EC2实例 端口 8888 自定义 tcp 规则 https 443 和 ssh 22 在 AWS 任何地方 上开放 我知道 chmod 400 key pem 和 ju
  • 如何在没有 (L)GPL 库的情况下在 Python 中创建双重身份验证 HTTPS 客户端?

    客户端和服务器都是内部的 各自都有一个由内部CA签名的证书和CA证书 我需要客户端根据服务器拥有的 CA 证书来验证服务器的证书 它还应该将其证书发送到服务器进行身份验证 The urllib2手册说不执行服务器身份验证 PycURL是一个
  • 如何使用 LDAP 身份验证使用 gitlab 设置管理员用户?

    我刚刚设置了 gitlab 但我完全迷失了管理员用户 维基百科似乎对这个话题保持沉默 谷歌也没有提供帮助 那么 如何在 LDAP 身份验证上使用 gitlab 设置管理员用户 您还可以通过在 Rails 控制台中执行以下操作来为用户设置管理
  • AWS AppSync 授权

    我计划使用 AWS Appsync 迁移 lambda 函数中的 graphQL 终端节点 该函数由 API 网关的 POST 触发 我研究 AppSync 主要是因为订阅 我无法使用 Lambda 函数创建订阅 我的身份验证机制基于 Au
  • Polymer 1.0:用户身份验证 UX(建议、教程和示例)?

    是否有推荐的 聚合物方式 来进行用户身份验证 这个问题包括技术部分和用户体验 用于进行用户身份验证的用户体验的全面示例 和 或教程 将非常棒 注意 Polymer 入门套件不包含任何身份验证 UX 示例 在这个问题中 用户体验 的意思是 完
  • 如何获取任何已安装 Android 应用程序的清单权限

    是否可以获得任何已安装的 Android 应用程序的清单权限 感谢您的提示 让它运行 final Intent mainIntent new Intent Intent ACTION MAIN null mainIntent addCate
  • ASP.Net MVC 4 通用主要难点

    我正在开发一个ASP NET MVC 4Web应用程序 以前我的 MVC 应用程序是使用MVC 3和这个新的MVC 4我刚刚复制 重复使用了我的应用程序认证和授权码从以前的应用程序 当用户登录我的网站时 我会执行以下操作 账户控制器 pub
  • laravel 4 登录验证失败

    在 Laravel4 中 我在路由中编写了以下代码 但它总是将我重定向到登录页面 我用谷歌搜索并在堆栈溢出上找到了它 并尝试了所有解决方案但没有成功 我确信这将是一个愚蠢的错误 但请跟踪它 谢谢 Routes Route post logi
  • 确定用于映射网络驱动器的域和用户名

    使用带有 SP1 的 Windows 7 Enterprise 但我希望得到适用于 Windows XP 2003 2008 Vista 7 的通用答案 从命令提示符处 我执行net use命令将 Z 驱动器映射到另一台计算机上的共享 但我
  • 在 React Native 应用程序中哪里可以获取 EXPO DEBUG 值?

    当我尝试登录 expo 时出现此错误 exp Set EXPO DEBUG true in your env to view the stack trace 你知道我到底要在哪里设置值吗EXPO DEBUG EXPO DEBUG是一个环境变
  • 使用 Vaadin Flow Web 应用程序处理全局所有布局和“路由”URL 的登录

    Vaadin 8 在 Vaadin 8 中 在我的UI子类我通过检查用户的会话是否带有一个属性来处理登录 该属性表明他们是否已成功登录 如果没有的话 我的UI子类显示登录布局 而不是带有导航选项的其他内容 例如菜单栏和在该 UI 内切换布局
  • 免费 PHP 登录库 [关闭]

    就目前情况而言 这个问题不太适合我们的问答形式 我们希望答案得到事实 参考资料或专业知识的支持 但这个问题可能会引发辩论 争论 民意调查或扩展讨论 如果您觉得这个问题可以改进并可能重新开放 访问帮助中心 help reopen questi
  • 为什么我只能用管理员权限才能导入Python中的某些模块?

    我正在努力解决 Python 2 7 中的一些奇怪问题 我写了一个很长的工具 在其中导入不同的模块 我必须首先使用它安装pip 该工具将在公司内部共享 不同的用户在其特定机器上拥有不同的权限 当另一个用户登录我的计算机 我在那里拥有管理员权
  • Flask 会话变量

    我正在用 Flask 编写一个小型网络应用程序 当两个用户 在同一网络下 尝试使用应用程序时 我遇到会话变量问题 这是代码 import os from flask import Flask request render template
  • 我可以使用passport-google回调来验证android/ios用户吗?

    我有一个使用 google passport oauth2 进行身份验证的 node js 服务器 我的服务器端代码看起来像这样文档 https www npmjs com package passport google oauth2 au
  • ExtJS:使用“记住我”功能登录

    我正在尝试创建一个具有非常常见的 记住我 功能的简单登录窗口 登录验证是通过 AJAX 风格完成的 因此浏览器不会记住我的输入 我的方法是使用内置的state功能 但是如何使用它让我很困惑 Ext state Manager setProv
  • 混合 ASP.NET WebForms 和 MVC 授权

    我正在尝试将一些 MVC3 功能混合到现有的 WebForms 应用程序中 我遵循了一些指南 除了授权部分之外 一切都已设置完毕并正常工作 现有的应用程序有

随机推荐