繁体   English   中英

AWS API Gateway - CORS“访问控制允许来源” - 多个条目

[英]AWS API Gateway - CORS "access-control-allow-origin" - multiple entries

我有一个连接到定义的 AWS API 网关的 AWS Lambda 实例。 如果我启用 CORS 并为access-control-allow-origin定义http://example.com ,那么我可以从http://example.com访问 Lambda 实例。 但是,如果我使用https://example.com ,它就不起作用。

那么在 AWS 中,如何在不使用通配符的情况下使用多个access-control-allow-origin值来定义? 我尝试使用*.example.com类的东西,但这不起作用。

编辑:如果我在 API 网关上使用'*'作为我的值,但在我的 S3 存储桶上设置 CORS 规则,那会安全吗? 存储桶规则示例:

<?xml version="1.0" encoding="UTF-8"?>
<CORSConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
    <CORSRule>
        <AllowedOrigin>http://example.com</AllowedOrigin>
        <AllowedMethod>GET</AllowedMethod>
        <AllowedMethod>POST</AllowedMethod>
        <AllowedMethod>PUT</AllowedMethod>
        <MaxAgeSeconds>3000</MaxAgeSeconds>
        <AllowedHeader>*</AllowedHeader>
    </CORSRule>
    <CORSRule>
        <AllowedOrigin>https://example.com</AllowedOrigin>
        <AllowedMethod>GET</AllowedMethod>
        <AllowedMethod>POST</AllowedMethod>
        <AllowedMethod>PUT</AllowedMethod>
        <MaxAgeSeconds>3000</MaxAgeSeconds>
        <AllowedHeader>*</AllowedHeader>
    </CORSRule>
    <CORSRule>
        <AllowedOrigin>https://www.example.com</AllowedOrigin>
        <AllowedMethod>GET</AllowedMethod>
        <AllowedMethod>POST</AllowedMethod>
        <AllowedMethod>PUT</AllowedMethod>
        <MaxAgeSeconds>3000</MaxAgeSeconds>
        <AllowedHeader>*</AllowedHeader>
    </CORSRule>
</CORSConfiguration>

如果您想启用多个 Origin,这一直是 CORS 的烦恼。

其他系统(例如 express/nginx 等)中的常见解决方法是:

  • 检查浏览器发送的Origin标头
  • 对照来源白名单检查它
  • 如果匹配,则将传入的Origin作为Access-Control-Allow-Origin标头返回,否则返回占位符(默认来源)

使用 AWS-Gateway 的自动装配 CORS 支持,因为使用模拟集成是不可能的,但是如果您编写自己的代码来处理OPTIONS请求,这是可能的。

下面是使用 lambda 代理集成编写的示例代码:

const allowedOrigins = [
    "http://example.com",
    "http://example.com:8080",
    "https://example.com",
    "https?://[a-z]*.?myapp.com",
    "http://localhost:[0-9]*"
];

exports.handler = (event, context) => {
    const origin = event.headers.Origin || event.headers.origin;
    var goodOrigin = false;

    if (origin) {
        allowedOrigins.forEach( allowedOrigin => {
            if (!goodOrigin && origin.match(allowedOrigin)) {
                goodOrigin = true;
            }
        });
    }

    context.succeed({
        headers: {
            "Access-Control-Allow-Headers": "Accept,Accept-Language,Content-Language,Content-Type,Authorization,x-correlation-id",
            "Access-Control-Expose-Headers": "x-my-header-out",
            "Access-Control-Allow-Methods": "DELETE,GET,HEAD,OPTIONS,PATCH,POST,PUT",
            "Access-Control-Allow-Origin": goodOrigin ? origin : allowedOrigins[0]
        },
        statusCode: 204
    });
};

将其保存为 lambda 函数。 要在 API-Gateway 中进行设置,请添加一个OPTIONS方法并为Integration Request选择Lambda Function并勾选Use Lambda Proxy integration

当然,这样做的缺点是您要为 lambda 函数付费,并且调用 lambda 函数可能会比模拟集成多出 50 毫秒的延迟。

为什么不使用 Velocity Template 语言映射模板从允许的域列表中检查并设置源头

$input.json("$")
#set($domains = ["https://www.example.com", "https://www.abcd.com"])
#set($origin = $input.params("origin"))
#if($domains.contains($origin))
#set($context.responseOverride.header.Access-Control-Allow-Origin="$origin")
#end

不幸的是,这在今天是不可能的。 CORS 规范不允许部分通配符,目前 API 网关只允许标头使用单个静态值。

您可以重载 OPTIONS 方法以根据传入的主机标头动态返回此值。

我做了这样的事情:

const handler: APIGatewayProxyHandler = async (event) => {
  const origin = event?.headers?.Origin || event?.headers?.origin;
  const allowedOrigins = ['https://example.com'];
  const headers = {
    'Access-Control-Allow-Origin': allowedOrigins.includes(origin)
      ? origin
      : allowedOrigins[0],
  };

  return {
    headers,
    body: JSON.stringify({
      myResponse: 'data',
    }),
    statusCode: 200,
  };
};

然后可以通过 chrome 开发工具进行测试,方法是转到您的客户端域并在控制台中运行 fetch:

fetch('https://exampleLambda.com/v1/example', { 
   method: 'get',
   mode: 'cors'
   headers: new Headers({
     'Authorization': 'Bearer 12345, 
   }), 
 })
 .then(result => result.json())
 .then(console.log)

我使用 API Gateway 和 Mock Integration 来检查原始标头是否具有 https 以及我信任的来自受信任域列表的原始域

  1. 在 API Gateway 资源中单击任何 OPTIONS 方法并转到集成请求
  2. 选择单选按钮 当没有定义模板时(推荐)
  3. 点击 Content Type 中的 application/json
{"statusCode": 200}
#set($domainsList = ['test.com','abc.in'])
#foreach( $domain in domainList)
    if(($input.params('origin').startsWith("https") && $input.params('origin').endsWith($domain) || $input.params('origin').endsWith("localhost") || $input.params('origin').endsWith("localhost:8100"))
    #set($context.responseOverride.header.Access-Control-Allow-Origin = $input.params('origin'))
    #break
    #end
#end

在 If 条件下,我还指定了 localhost,因为我必须从 localhost:8100 测试我的应用程序,而对于 Android 应用程序,Origin 标头是 https://localhost

我们可以在 Apache Velocity 模板中使用基于 java 的比较器

-> 参考: https ://velocity.apache.org/engine/1.7/user-guide.html

您可以让API Gateway返回以逗号分隔的域列表,但浏览器只需要一个。 这在其他答案中暗示,只是明确说明。

以下是浏览器只接受CORS标题中的一个域的参考: https//developer.mozilla.org/en-US/docs/Web/HTTP/CORS/Errors/CORSMultipleAllowOriginNotAllowed

而且只是为了踢,这里是你如何返回多个(虽然不知道为什么你想要)。 在您的逗号分隔列表周围添加单引号。 我只是做了一个我的API并且它工作(因为它返回多个域和API中断)。

首先点击这里:

在此输入图像描述

填写如下(使用您的值):

在此输入图像描述

请注意仅在开头和结尾使用单引号。 您将返回多个域,但浏览器不会进行下一次调用,因为它不会解析它们以进行匹配。

这是POSTman的截图,显示标题中返回的两个值: 在此输入图像描述

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM