AWS API 网关 - CORS "access-control-allow-origin" - 多个条目



我有一个AWS Lambda实例,它连接到一个定义的AWS API网关。如果我启用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函数可能会比模拟集成多50ms的延迟。

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

$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网关来检查源标头是否有https,以及我从可信域列表中信任的源域

  1. 在API网关资源中单击任何选项方法并转到集成请求
  2. 选择单选按钮当没有定义模板时(推荐)
  3. 点击Content Type
  4. 中的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 Template中使用基于java的比较器

→参考资料:https://velocity.apache.org/engine/1.7/user-guide.html

相关内容

  • 没有找到相关文章

最新更新