Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/node.js/41.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Node.js 如何从lambda调用IAM授权的AWS ApiGateway端点?_Node.js_Aws Lambda_Aws Api Gateway_Amazon Iam_Apollo Server - Fatal编程技术网

Node.js 如何从lambda调用IAM授权的AWS ApiGateway端点?

Node.js 如何从lambda调用IAM授权的AWS ApiGateway端点?,node.js,aws-lambda,aws-api-gateway,amazon-iam,apollo-server,Node.js,Aws Lambda,Aws Api Gateway,Amazon Iam,Apollo Server,我正在尝试从lambda函数调用AWS ApiGateway HTTP端点,我已经使用IAM授权程序对其进行了保护,但是我无法从lambda函数中获得任何工作 我已经用Postman测试了端点,当我选择“AWS签名”作为授权类型并放入本地凭据时,可以确认它是否有效,因此端点的设置方式没有问题。这一定是我如何从Lambda发送请求的问题。另一个挑战是将头添加到GraphQLAPI请求中 这就是我的lambda函数的样子: import { ApolloServer } from 'apollo-s

我正在尝试从lambda函数调用AWS ApiGateway HTTP端点,我已经使用IAM授权程序对其进行了保护,但是我无法从lambda函数中获得任何工作

我已经用Postman测试了端点,当我选择“AWS签名”作为授权类型并放入本地凭据时,可以确认它是否有效,因此端点的设置方式没有问题。这一定是我如何从Lambda发送请求的问题。另一个挑战是将头添加到GraphQLAPI请求中

这就是我的lambda函数的样子:

import { ApolloServer } from 'apollo-server-lambda';
import { APIGatewayProxyEvent, Callback, Context } from 'aws-lambda';
import { ApolloGateway, RemoteGraphQLDataSource } from '@apollo/gateway';
import aws4 from 'aws4';

const userServiceUrl = process.env.USER_SERVICE_URL;
const {hostname, pathname} = new URL(userServiceUrl);

class AuthenticatedDataSource extends RemoteGraphQLDataSource {
  willSendRequest({request}) {
    console.log('request is: ', request);
    const opts: Record<string, any> = {
      service: 'execute-api',
      region: process.env.AWS_REGION,
      host: hostname,
      path: pathname,
      body: JSON.stringify({query: request.query}),
      method: 'POST'
    }
    aws4.sign(opts);
    console.log('opts are: ', opts);
    request.http.headers.set('X-Amz-Date', opts.headers['X-Amz-Date']);
    request.http.headers.set('Authorization', opts.headers['Authorization']);
    request.http.headers.set('X-Amz-Security-Token', opts.headers['X-Amz-Security-Token']);
  }
}
从“阿波罗服务器lambda”导入{ApolloServer};
从“aws lambda”导入{APIGatewayProxyEvent,回调,Context};
从“@apollo/gateway”导入{ApolloGateway,RemoteGraphQLDataSource};
从“aws4”导入aws4;
const userServiceUrl=process.env.USER\u SERVICE\u URL;
const{hostname,pathname}=新URL(userServiceUrl);
类AuthenticatedDataSource扩展了RemoteGraphQLDataSource{
willSendRequest({request}){
log('请求为:',请求);
常数选项:记录={
服务:“执行api”,
地区:process.env.AWS_地区,
主机名:,
路径:路径名,
body:JSON.stringify({query:request.query}),
方法:“发布”
}
aws4.标志(opts);
log('opts为:',opts);
request.http.headers.set('X-Amz-Date',opts.headers['X-Amz-Date']);
request.http.headers.set('Authorization',opts.headers['Authorization']);
request.http.headers.set('X-Amz-Security-Token',opts.headers['X-Amz-Security-Token']);
}
}
无论我尝试什么,我总是得到一个403禁止的错误,并且请求从未到达授权者后面的实际端点。我尝试过移除尸体,我尝试过将我的本地凭证硬编码到aws4调用中,但都不起作用。我的直觉是,我的签名电话不知何故是错的,但当我把它与我在互联网上找到的几个例子进行比较时,我看不出有什么明显的错误


任何能为我指明正确方向的资源都将不胜感激。我发现的大多数示例都是前端特定的,因此我知道这可能会引导我出错。

调用
willSendRequest
函数不是签署请求的最佳位置,因为apollo server可以在调用
willSendRequest
后修改请求对象。 相反,您应该实现一个自定义获取,并将其传递给
RemoteGraphQLDataSource
构造函数,以确保在发送最终请求之前对其进行签名

您的自定义
GraphQLDataSource
与自定义获取类似:

import { Request, RequestInit, Response, fetch, Headers } from "apollo-server-env";
import aws4 from 'aws4';
import { RemoteGraphQLDataSource } from '@apollo/gateway';

class AuthenticatedDataSource extends RemoteGraphQLDataSource {
    public constructor(
        url: string,
    ) {
        super({
            url: url,
            fetcher: doFetch,
        });
    }

    async doFetch(
        input?: string | Request | undefined,
        init?: RequestInit | undefined
    ): Promise<Response> {
        const url = new URL(input as string);
        const opts: Record<string, any> = {
            service: 'execute-api',
            region: process.env.AWS_REGION,
            host: url.hostname,
            path: url.pathname,
            body: init?.body,
            method: init?.method
        }
        aws4.sign(opts);
        init.headers.set('X-Amz-Date', opts.headers['X-Amz-Date']);
        init.headers.set('Authorization', opts.headers['Authorization']);
        init.headers.set('X-Amz-Security-Token', opts.headers['X-Amz-Security-Token']);
        const response = await fetch(input, init);

        return response;
    }
}
import{Request,RequestInit,Response,fetch,Headers}来自“apollo服务器环境”;
从“aws4”导入aws4;
从“@apollo/gateway”导入{RemoteGraphQLDataSource};
类AuthenticatedDataSource扩展了RemoteGraphQLDataSource{
公共构造函数(
url:string,
) {
超级({
url:url,
fetcher:doFetch,
});
}
异步doFetch(
输入?:字符串|请求|未定义,
init?:RequestInit |未定义
):承诺{
const url=新url(作为字符串输入);
常数选项:记录={
服务:“执行api”,
地区:process.env.AWS_地区,
主机:url.hostname,
路径:url.pathname,
body:init?.body,
方法:init?方法
}
aws4.标志(opts);
init.headers.set('X-Amz-Date',opts.headers['X-Amz-Date']);
init.headers.set('Authorization',opts.headers['Authorization']);
init.headers.set('X-Amz-Security-Token',opts.headers['X-Amz-Security-Token']);
const response=等待获取(输入,初始化);
返回响应;
}
}

为了繁荣,我最终做到了这一点,并且工作完美无瑕(非常感谢格伦·托马斯!)

import{Request,RequestInit,Response,fetch}来自“阿波罗服务器环境”;
从'apollo server lambda'导入{apollo server};
从“aws lambda”导入{APIGatewayProxyEvent,回调,Context};
从“@apollo/gateway”导入{ApolloGateway,RemoteGraphQLDataSource};
从“aws4”导入aws4;
const userServiceUrl=process.env.USER\u SERVICE\u URL;
异步函数doFetch(
输入?:请求|字符串,
init?:RequestInit
):承诺{
const urlString=typeof input=='string'?input:input.url;
const url=新url(urlString);
常数选项:记录={
服务:“执行api”,
地区:process.env.AWS_地区,
主机:url.hostname,
路径:url.pathname,
body:init?.body,
方法:init?方法
}
aws4.标志(opts);
init.headers=opts.headers;
const response=等待获取(输入,初始化);
返回响应;
}
类AuthenticatedDataSource扩展了RemoteGraphQLDataSource{
构造函数(url:string){
超级({
网址,
取数器:doFetch
})
}
}
const server=新服务器({
网关:新阿波罗网关({
服务列表:[
{name:'users',url:userServiceUrl}
],
构建服务({url}){
返回新的AuthenticatedDataSource(url)
}
}),
订阅:false,
反省:没错,
操场:没错,
});
导出常量处理程序=(事件:APIGatewayProxyEvent,上下文:上下文,回调:回调)=>{
log('event is:',JSON.stringify(event,null,2))
return server.createHandler({
cors:{
来源:'*'
}
})(事件、上下文、回调);
}

非常感谢,我终于让它工作了!在将其更改为此之后,我能够通过覆盖所有标题而不是仅覆盖我设置的3个标题使其工作。(请参阅我的答案了解我所做的更改)对于发现此问题的任何人来说,另一个建议是始终使用通配符IAM策略进行测试,然后在其正常工作后使其更加严格。我想我可能是因为使用了我认为有效的资源限定符而把事情搞砸了。
import { Request, RequestInit, Response, fetch } from "apollo-server-env";
import { ApolloServer } from 'apollo-server-lambda';
import { APIGatewayProxyEvent, Callback, Context } from 'aws-lambda';
import { ApolloGateway, RemoteGraphQLDataSource } from '@apollo/gateway';
import aws4 from 'aws4';

const userServiceUrl = process.env.USER_SERVICE_URL;

async function doFetch(
  input?: Request | string,
  init?: RequestInit
): Promise<Response> {
  const urlString = typeof input === 'string' ? input : input.url;
  const url = new URL(urlString);
  const opts: Record<string, any> = {
    service: 'execute-api',
    region: process.env.AWS_REGION,
    host: url.hostname,
    path: url.pathname,
    body: init?.body,
    method: init?.method
  }
  aws4.sign(opts);
  init.headers = opts.headers;
  const response = await fetch(input, init);

  return response;
}

class AuthenticatedDataSource extends RemoteGraphQLDataSource {
  constructor(url: string) {
    super({
      url,
      fetcher: doFetch
    })
  }
}

const server = new ApolloServer({
  gateway: new ApolloGateway({
    serviceList: [
      { name: 'users', url: userServiceUrl }
    ],
    buildService({url}) {
      return new AuthenticatedDataSource(url)
    }
  }),
  subscriptions: false,
  introspection: true,
  playground: true,
});

export const handler = (event: APIGatewayProxyEvent, context: Context, callback: Callback) => {
  console.log('event is: ', JSON.stringify(event, null, 2))
  
  return server.createHandler({
    cors: {
      origin: '*'
    }
  })(event, context, callback);
}