寻找通过http请求从NodeJS应用程序访问AWS Appsync Graphql API的指南

c3frrgcw  于 11个月前  发布在  Node.js
关注(0)|答案(2)|浏览(83)

据我所知,目前还没有一个指南可以完成这个过程,所以我想找到一个可以扩展的指南,并由其他可能有额外安全“最佳实践”和喜欢贡献的人评论。

xyhw6mcr

xyhw6mcr1#

通过NodeJS中的IAM用户访问AWS Appsync API

我们将涵盖

  • 设置本地env以构建和部署放大
  • 设置Amplify后端应用程序
  • 将graphql API添加到我们的应用程序
  • 为AWS IAM配置适当的权限以访问我们的API
  • 配置我们的应用程序以允许IAM用户访问API
  • 为从NodeJS向API发出的请求构建签名函数

这应该涵盖在AppSync中设置graphql API(使用Amplify)的整个过程,该过程将由外部NodeJS应用通过AWS_IAM auth使用。

预设

这里假设您已经设置了一个AWS帐户,如果不是这样,您需要前往这里设置一个:

  • https://portal.aws.amazon.com/billing/signup#/start/email

要开始使用,您需要从AWS转到此文档,并设置您的本地计算机以构建和部署Amplify后端。请注意,虽然某些设置可以从Amplify Studio完成,但设置IAM权限的配置必须从CLI完成。
入门:https://docs.amplify.aws/lib/project-setup/prereq/q/platform/js/

设置后端应用

设置完成后,我们将创建一个新的应用程序,基本上遵循本指南的“初始化新后端”部分:

  • https://docs.amplify.aws/lib/project-setup/create-application/q/platform/js/#initialize-a-new-backend
mkdir amplify-api
  cd amplify-api

  amplify init

字符串

设置API

接下来,我们将向我们的应用程序添加一个API,并相应地配置它,所以回到我们的:

amplify add api

  • 当提示您进行配置选择时,您将希望选择“Graphql”,尽管理论上本指南也应该完全适用于REST。
  • 接下来你需要 * 修改“授权模式”选项。默认情况下,这将被设置为“API key”,但是我们需要将其更改为“IAM”,现在不要担心设置任何其他的授权类型。
  • 然后选择“Blank Schema”并选择“Yes”来编辑该模式。这将在预定义的编辑器中打开“schema.graphql”文件,我们将在其中添加一些代码,使其看起来像这样:
# This "input" configures a global authorization rule to enable public access to
# all models in this schema. Learn more about authorization rules here: https://docs.amplify.aws/cli/graphql/authorization-rules
input AMPLIFY { globalAuthRule: AuthRule = { allow: public } } # FOR TESTING ONLY!

type Todo @model @auth(rules: [{ allow: private, provider: iam, operations: [read, create, update] }]) {
  id: ID!
  title: String!
  status: Boolean
}


您可以将此模式配置为任何您喜欢的外观,主要是对于您希望能够从API外部访问的每个模型,您需要 * 确保“@auth”规则与上面的规则匹配,更新您希望包含/忽略的任何操作。
我们现在需要部署我们的API,运行:

amplify push


当出现“你确定要继续吗?”时,选择“是”,至于后面的其他提示,你可以选择“否”。
这将在AWS端执行许多操作,包括在DynamoDB中创建数据库,设置与我们之前设置的模式对应的表,并为所有这些资源分配权限。

设置IAM用户

在您的浏览器中,转到您的AWS帐户并导航到IAM部分,在这里我们将添加一个新用户“amplify-api-user”-或者您喜欢的任何用户。
点击下一步,然后从3个选项中,选择“直接附加策略”,然后点击右上角的“创建策略”。
应该会打开一个新窗口,您现在可以为此用户创建一个安全策略,限制他们只能访问他们需要的AWS资源。在我们的示例中,此策略如下所示(使用JSON编辑器):

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "appsync:GraphQL"
            ],
            "Resource": "{APPSYNC_API_ARN}/*"
        }
    ]
}


要访问您的{APPSYNC_API_ARN},您需要打开一个新窗口并转到AWS中的AppSync。选择我们之前创建的API“amplify-API”,然后在左侧选择“settings”。您应该会看到一个标题为“API ARN”的字段,复制此值并将其插入到上面的策略中。它应该如下所示:

arn:aws:appsync:ap-southeast-2:xxxxxxxxxxxxxx:apis/xxxxxxxxxxxxxxxxx

在我们的应用中授予IAM用户权限

现在是能够通过我们的IAM用户访问我们的API的关键步骤。在我们本地机器上的amplify-API文件夹中,我们应该有一个类似于以下的文件夹结构:

- amplify/
 | - backend/
   | - api/
     | - amplifyapi/
       | - build/
       | - schema.graphql
       | - ...
   | - types/
   | - backend-config.json
   | - ...
 | - hooks/
 | cli.json
 | ...
- src/
 | - aws-exports.js


在目录“/amplify-api/amplify/backend/api/amplifyapi/”中,我们需要添加一个名为“custom-roles.json”的新文件:

{
    "adminRoleNames": ["{AWS_IAM_ARN}"]
  }


与之前的“APPSYNC_API_ARN”一样,我们需要获取IAM用户的ARN ID。因此,在浏览器中导航到IAM的用户部分,选择您的用户并复制ARN值,该值应该如下所示:

arn:aws:iam::xxxxxxxxxxx:user/amplify-api-user


一旦此文件已添加到我们的应用程序中,我们可以再次推送这些更改:

amplify push

为IAM用户创建访问密钥

设置AWS的最后一步是为IAM用户添加访问密钥。我们将在稍后发出请求时使用这些密钥作为主授权令牌。
前往AWS的IAM部分,再次选择您的用户。点击“安全凭证”,然后向下滚动到“创建访问密钥”。在“用例”中,您选择什么并不重要,但我们将使用“第三方服务”。如果您愿意,请添加一个dec,然后您将被带到一个带有“访问密钥”和“秘密访问密钥”的屏幕。
将这些文件存储在一个安全的地方,或者下载.csv文件,然后我们就完成了。

创建请求函数

最后,我们现在可以测试所有这些艰苦的工作了,好吧,几乎。我们需要提供一种方法来使用我们刚刚设置的访问密钥对向我们的API发出的请求进行签名。这将是我们在运行Nodejs的外部应用程序上设置的代码,以调用我们的API。
下面的代码片段是在这些资源中找到的代码的修改版本:

如果没有这本指南将不可能如此巨大的感谢这些作者!
首先,您需要安装几个包,这些库用于创建我们将在fetch请求中发送给AWS的authed对象:

npm i @smithy/signature-v4 @smithy/protocol-http @aws-crypto/sha256-js


一旦这些都安装好了,我们就可以导入它们并开始构建请求对象:

import { SignatureV4 } from '@smithy/signature-v4'
import { HttpRequest } from '@smithy/protocol-http'
import { Sha256 } from '@aws-crypto/sha256-js'

const {
  API_URL,
  AWS_ACCESS_KEY_ID,
  AWS_SECRET_ACCESS_KEY
} = process.env;

const apiUrl = new URL(API_URL!)

const signer = new SignatureV4({
  service: 'appsync',
  region: 'ap-southeast-2',
  credentials: {
    accessKeyId: AWS_ACCESS_KEY_ID!,
    secretAccessKey: AWS_SECRET_ACCESS_KEY!
  },
  sha256: Sha256,
})

export const signedFetch = async (graphqlObject) => {

  if (!graphqlObject) return

  // set up the HTTP request
  const request = new HttpRequest({
    hostname: apiUrl.host,
    path: apiUrl.pathname,
    body: JSON.stringify(graphqlObject),
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      host: apiUrl.hostname
    },
  })

  const signedRequest = await signer.sign(request)

  const { headers, body, method } = await signedRequest

  const awsSignedRequest = await fetch(API_URL!, {
    headers,
    body,
    method
  }).then((res) => res.json())

  return awsSignedRequest
}


从“process.env”调用的变量属于我们之前创建的“访问密钥”和“秘密访问密钥”,“API_URL”指的是我们的Graphql API URL,我们可以从Appsync中的“GraphQL端点”下的API中获取。
然后使用此函数向Graphql API发出请求:

const MyGraphqlQuery =  {
  query: `
    query getTodos {
      listTodos {
        items {
          title
          status
        }
      }
    }
  `
}

const response = signedRequest(MyGraphqlQuery).then((res) => res)


这一切都可以 Package 在一组函数/文件中,或者拆分出来,但最适合您的应用结构。

kyks70gy

kyks70gy2#

Jezza的上述答案非常好,对我来说非常完美。然而,我在使用这个解决方案时偶然发现了另外2个错误。

**1.浏览器抛出警告:*拒绝设置不安全头“host”

我修复了在使用delete headers["host"]进行请求之前删除头部的问题

...
const signedRequest = signer.sign(signingRequest);
const { headers, body, method } = await signedRequest;
// otherwise browser says 'Refused to set unsafe header "host"'
delete headers["host"];
const awsSignedRequest = await fetch(API_URL!, {
  headers,
  body,
  method
}).then((res) => res.json())
...

字符串

2.使用Query params签名请求对我不起作用。

以下解决方案解决了以下问题:

// set up the HTTP request
  const request = new HttpRequest({
    hostname: apiUrl.host,
    path: apiUrl.pathname,
    body: JSON.stringify(graphqlObject),
    query: Object.fromEntries(apiUrl.searchParams),
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      host: apiUrl.hostname
    },
  })

  const signedRequest = await signer.sign(request)

相关问题