API网关CORS:没有'访问控制 - 允许 - 来源'头

时间:2016-02-04 00:51:23

标签: ajax amazon-web-services cors aws-api-gateway

虽然已通过API网关设置了CORS并且设置了Access-Control-Allow-Origin标头,但在尝试从Chrome中的AJAX调用API时仍会收到以下错误:

  

XMLHttpRequest无法加载http://XXXXX.execute-api.us-west-2.amazonaws.com/beta/YYYYY。 No' Access-Control-Allow-Origin'标头出现在请求的资源上。起源' null'因此不允许访问。响应的HTTP状态代码为403。

我尝试通过Postman获取网址,并显示上述标题已成功通过:

Passed headers

来自OPTIONS的回应:

Response headers

如何在不恢复为JSON-P的情况下从浏览器调用我的API?

22 个答案:

答案 0 :(得分:69)

我遇到了同样的问题。我用了10个小时才发现。

https://serverless.com/framework/docs/providers/aws/events/apigateway/

// handler.js

'use strict';

module.exports.hello = function(event, context, callback) {

const response = {
  statusCode: 200,
  headers: {
    "Access-Control-Allow-Origin" : "*", // Required for CORS support to work
    "Access-Control-Allow-Credentials" : true // Required for cookies, authorization headers with HTTPS 
  },
  body: JSON.stringify({ "message": "Hello World!" })
};

callback(null, response);
};

答案 1 :(得分:47)

如果还有其他人遇到这种情况 - 我能够在应用程序中找到根本原因。

如果您使用自定义授权程序运行API-Gateway,API-Gateway将在实际命中服务器之前发送401或403。默认情况下 - 从自定义授权程序返回4xx时,未为CORS配置API网关。

此外 - 如果您恰好从通过API网关的请求中获取了01的状态代码,则可能是您的问题。

要修复 - 在API网关配置中 - 转到"网关响应",展开"默认4XX"并在那里添加一个CORS配置头。即。

Access-Control-Allow-Origin: '*'

确保重新部署您的网关 - 瞧!

答案 2 :(得分:9)

1)我需要和@riseres一样做以及其他一些更改。这是我的回复标题:

headers: {
            'Access-Control-Allow-Origin' : '*',
            'Access-Control-Allow-Headers':'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token',
            'Access-Control-Allow-Credentials' : true,
            'Content-Type': 'application/json'
        }

2)

根据此文件:

http://docs.aws.amazon.com/apigateway/latest/developerguide/how-to-cors.html

在API网关配置上使用代理进行lambda函数时,post或get方法没有添加标题,只有选项可以。您必须在响应中手动执行(服务器或lambda响应)。

3)

除此之外,我还需要在API网关帖子方法中禁用“API Key Required”选项。

答案 3 :(得分:9)

让我的示例正常工作:我只是标题中插入'Access-Control-Allow-Origin':'*',:{} 在生成的nodejs Lambda函数中。我对Lambda生成的API层进行了 no 更改。

这是我的NodeJS:

'use strict';
const doc = require('dynamodb-doc');
const dynamo = new doc.DynamoDB();
exports.handler = ( event, context, callback ) => {
    const done = ( err, res ) => callback( null, {
        statusCode: err ? '400' : '200',
        body: err ? err.message : JSON.stringify(res),
        headers:{ 'Access-Control-Allow-Origin' : '*' },
    });
    switch( event.httpMethod ) {
        ...
    }
};

这是我的AJAX电话

$.ajax({
    url: 'https://x.execute-api.x-x-x.amazonaws.com/prod/fnXx?TableName=x',
    type: 'GET',
    beforeSend: function(){ $( '#loader' ).show();},
    success: function( res ) { alert( JSON.stringify(res) ); },
    error:function(e){ alert('Lambda returned error\n\n' + e.responseText); },
    complete:function(){ $('#loader').hide(); }
});

答案 4 :(得分:7)

我刚刚在lambda函数响应中添加了标头,它的工作原理就像一个魅力

var arr = [{name: "a1", value: 1},{name: "a3", value: 1},{name: "a4", value: 1},{name: "a2", value: 1}];

var arr2 = [{name: "a1", value: 1},{name: "a3", value: 1},{name: "a14", value: 1},{name: "a12", value: 1}];

var arr3 = [{name: 1, value: 1},{name: 11, value: 1},{name: 14, value: 1},{name: 12, value: 1}];
var sort = function (prop, arr) {  
    prop = prop.split('.');
    var len = prop.length;
    
    arr.sort(function (a, b) {
        var i = 0;
        while( i < len ) {
            a = a[prop[i]];
            b = b[prop[i]];
            i++;
        }
        let [key1,digit1] = (""+a).split(/(?<=[a-z])(?=\d)/)
        let [key2,digit2] = (""+b).split(/(?<=[a-z])(?=\d)/)
        return (key1 - key2) || (+digit1 - +digit2)
    });
    return arr;
};
arr = sort('name', arr);
arr2 = sort('name', arr2);
arr3 = sort('name', arr3)
console.log(arr); // it's correct
console.log(arr2); // it's not correct
console.log(arr3);

答案 5 :(得分:3)

对我来说,最终有效的答案是Alex R的答案中James Shapiro的评论(第二高的评价)。首先,我试图通过在S3中托管一个静态网页来使用lambda处理与我们联系的页面并发送电子邮件来解决这个API网关问题。只需检查[]默认4XX,即可修复错误消息。

enter image description here

答案 6 :(得分:2)

当我意识到lambda授权者失败并且由于某种未知的原因被转化为CORS错误后,我开始工作了。对我的授权人的一个简单修复(以及我应该首先添加的一些授权人测试)就可以了。对我来说,需要API网关操作“启用CORS”。这样就在API中添加了我需要的所有标头和其他设置。

答案 7 :(得分:2)

POSTOPTIONS启用CORS之后部署代码对我来说很有效。

答案 8 :(得分:1)

如果您尝试了所有与该问题无关的尝试,那么您将以我所做的一切而告终。事实证明,亚马逊现有的CORS设置说明工作正常……只需确保您记住要重新部署! CORS编辑向导即使带有所有漂亮的绿色小标记,也不会实时更新您的API。也许很明显,但是它让我难过了半天。

enter image description here

答案 9 :(得分:1)

对我来说,因为我使用的是非常标准的 React fetch 调用,所以这可以使用上面的一些 AWS 控制台和 Lambda 修复来修复,但是我的 Lambda 返回了正确的标头(我也使用了代理模式),我需要将我的应用程序打包成一个 SAM 模板,所以我不能花时间在控制台上点击。

我注意到所有 CORS 内容都运行良好,直到我将 Cognito Auth 放到我的应用程序中。我只是在使用越来越多的配置进行 SAM 包/SAM 部署时速度非常慢,直到它坏了,只要我将 Auth 添加到我的 API 网关,它就坏了。我花了一整天的时间点击像这样的精彩讨论,寻找一个简单的解决方法,但最终不得不实际阅读 CORS 正在做什么。我会为您节省阅读时间,并为您提供另一个简单的解决方案(至少对我而言)。

以下是最终有效的 API 网关模板示例 (YAML):

Resources:
  MySearchApi:
    Type: AWS::Serverless::Api
    Properties:
      StageName: 'Dev'
      Cors:
        AllowMethods: "'OPTIONS, GET'"
        AllowHeaders: "'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token'"
        AllowOrigin: "'*'"
      Auth:
        DefaultAuthorizer: MyCognitoSearchAuth
        Authorizers:
          MyCognitoSearchAuth:
            UserPoolArn: "<my hardcoded user pool ARN>"
            AuthType: "COGNITO_USER_POOLS"
        AddDefaultAuthorizerToCorsPreflight: False

注意底部的 AddDefaultAuthorizerToCorsPreflight。根据我的阅读,如果您的模板中没有它,则默认为 True。并且,当为 True 时,它​​会阻止正常的 OPTIONS 行为,以宣布资源在允许的来源方面支持什么。一旦我明确添加它并将其设置为 False,我的所有问题都解决了。

这意味着如果您遇到此问题并想要更完整地诊断它,您应该访问 API Gateway 中的资源并检查您的 OPTIONS 方法是否包含某种形式的身份验证。您的 GET 或 POST 需要 Auth,但如果您的 OPTIONS 启用了 Auth,那么您可能会发现自己处于这种情况。如果您在 AWS 控制台周围单击,请尝试从 OPTIONS 中删除,重新部署,然后进行测试。如果您使用的是 SAM CLI,请尝试我上面的修复。

答案 10 :(得分:1)

对于Google员工:

这是原因:

  • 简单请求或没有Cookie的GET / POST不会发送预检
  • API网关只能将预检或OPTIONS配置为使用模拟响应发送Allow-Origin标头
  • 如果您尝试在启用CORS模式的情况下发送简单请求,则会出错,因为该响应没有Allow-Origin标头
  • 您可能会遵循最佳做法,简单的请求并不意味着向用户发送响应,发送身份验证/ cookie以及您的请求以使其“不简单”,并且会触发预检
  • 不过,您必须为OPTIONS之后的请求自行发送CORS标头

答案 11 :(得分:1)

更改功能或代码后,请遵循以下两个步骤。

首先启用CORS ,然后部署API

答案 12 :(得分:0)

对于在 API Gateway 中使用 Cognito 授权器的用户,实际上无需设置自定义网关响应。 API 网关会阻止预检,因为它们在默认的 AWS 逻辑中是“未经授权的”。

幸运的是,有一个内置参数可以解决这个问题。只需将 AddDefaultAuthorizerToCorsPreflight: False 添加到您的 API Authorizer 中,API Gateway 将禁用飞行前请求的身份验证。这是 documentation 和示例设置:

MyApi:
Type: AWS::Serverless::Api
Properties:
  StageName: Prod
  Cors: 
    AllowHeaders: "'*'"
    AllowMethods: "'*'"
    AllowOrigin: "'*'"
  Auth:
    DefaultAuthorizer: MyCognitoAuthorizer
    AddDefaultAuthorizerToCorsPreflight: False
    Authorizers:
      MyCognitoAuthorizer:
        UserPoolArn: !GetAtt MyCognitoUserPool.Arn

答案 13 :(得分:0)

确保您调用的是正确的路径。

无论出于何种原因,命中不存在的路径都可能导致 CORS 相关错误。可能是因为 404 在其响应中不包含 CORS 标头。

感谢@jackko 对最初问题的评论。这是我的问题。听起来很傻,但可能发生在任何人身上。

答案 14 :(得分:0)

就我而言,我启用了所有方法和网关响应。然后它就像一个魅力。不要忘记部署。

enter image description here

答案 15 :(得分:0)

除了其他注释外,还需要注意的是从基础集成返回的状态,以及是否为该状态返回了Access-Control-Allow-Origin标头。

执行“启用CORS”操作只会设置200个状态。如果端点上还有其他对象,例如4xx和5xx,则需要自己添加标头。

答案 16 :(得分:0)

此问题的另一个根本原因可能是HTTP / 1.1和HTTP / 2之间的差异。

症状:某些用户(并非所有人)报告在使用我们的软件时收到CORS错误。

问题Access-Control-Allow-Origin标头有时 丢失。

上下文:我们有一个Lambda,专用于处理OPTIONS请求并使用相应的CORS标头进行回复,例如与白名单{{1}匹配的Access-Control-Allow-Origin }。

解决方案:对于HTTP / 2调用,API网关似乎将所有标头都转换为小写,但是对于HTTP / 1.1则保持大写。这导致对Origin的访问失败。

检查您是否也遇到此问题:

假设您的API位于event.headers.origin,而您的前端位于https://api.example.com。使用CURL,使用HTTP / 2发出请求:

https://www.example.com

响应输出应包含标题:

curl -v -X OPTIONS -H 'Origin: https://www.example.com' https://api.example.com

使用HTTP / 1.1(或使用小写的< Access-Control-Allow-Origin: https://www.example.com标头)重复同一步骤:

Origin

如果缺少curl -v -X OPTIONS --http1.1 -H 'Origin: https://www.example.com' https://api.example.com 标头,则在读取Access-Control-Allow-Origin标头时可能要检查是否区分大小写。

答案 17 :(得分:0)

就我而言,由于我使用AWS_IAM作为Authorization方法,因此我需要授予IAM角色访问终端的权限。

答案 18 :(得分:0)

我正在运行aws-serverless-express,在我的情况下需要编辑simple-proxy-api.yaml

在将CORS配置为https://example.com之前,我只是交换了网站的名称,并通过npm run setup重新部署,并更新了我现有的lambda / stack。

#...
/:
#...
method.response.header.Access-Control-Allow-Origin: "'https://example.com'"
#...
/{proxy+}:
method.response.header.Access-Control-Allow-Origin: "'https://example.com'"
#...

答案 19 :(得分:-1)

就我而言,我只是在错误地编写提取请求网址。在serverless.yml上,将cors设置为true

register-downloadable-client:
    handler: fetch-downloadable-client-data/register.register
    events:
      - http:
          path: register-downloadable-client
          method: post
          integration: lambda
          cors: true
          stage: ${self:custom.stage}

,然后在lambda处理程序上发送标头,但是如果在前端使提取请求出错,则不会在响应上获取该标头,并且会得到此错误。因此,请在前面仔细检查您的请求URL。

答案 20 :(得分:-2)

在Python中,您可以按照以下代码进行操作:

{ "statusCode" : 200,
'headers': 
    {'Content-Type': 'application/json',
    'Access-Control-Allow-Origin': "*"
     },
"body": json.dumps(
    {
    "temperature" : tempArray,
    "time": timeArray
    })
 }

答案 21 :(得分:-2)

我在

中找到了一个简单的解决方案

API网关>选择您的API端点>选择方法(在我的情况下是POST)

现在有一个下拉菜单ACTIONs> Enable CORS ..选择它。

现在再次选择下拉菜单操作>部署A​​PI(重新部署)

enter image description here

成功了!