如何正确配置S3 + Cloudfront的CORS?

113

我的应用程序将图片存储在 S3 上,然后通过 Cloudfront 进行代理。我很想使用新的S3 CORS支持,以便我可以使用 HTML5 画布方法(这些方法具有跨域策略),但是好像无法正确配置我的S3Cloudfront。当我尝试将图像转换为画布元素时仍然会遇到"Uncaught Error: SECURITY_ERR: DOM Exception 18"

以下是我目前的配置:

S3

<CORSConfiguration>
  <CORSRule>
    <AllowedOrigin>MY_WEBSITE_URL</AllowedOrigin>
    <AllowedMethod>GET</AllowedMethod>
    <MaxAgeSeconds>3000</MaxAgeSeconds>
    <AllowedHeader>*</AllowedHeader>
  </CORSRule>
  <CORSRule>
    <AllowedOrigin>MY_CLOUDFRONT_URL</AllowedOrigin>
    <AllowedMethod>GET</AllowedMethod>
    <AllowedHeader>*</AllowedHeader>
    </CORSRule>
  </CORSConfiguration>

Cloudfront

起源

Origin Protocol Policy: Match Viewer

HTTP Port: 80

HTTPS Port: 443

行为

Origin: MY_WEBSITE_URL

Object Caching: Use Origin Cache Headers

Forward Cookies: None

Forward Query Strings: Yes

我是否漏了什么?

更新: 刚试过将头文件更改为

<AllowedHeader>Content-*</AllowedHeader>
<AllowedHeader>Host</AllowedHeader>

基于这个问题Amazon S3 CORS(跨源资源共享)和Firefox跨域字体加载

仍然无法使用。

更新:有关请求的更多信息。

Request
URL:https://d1r5nr1emc2xy5.cloudfront.net/uploaded/BAhbBlsHOgZmSSImMjAxMi8wOS8xMC8xOC81NC80Mi85NC9ncmFzczMuanBnBjoGRVQ/32c0cee8
Request Method:GET
Status Code:200 OK (from cache)

更新

我认为我的请求可能不正确,所以我尝试使用以下方法启用CORS:

img.crossOrigin = '';

但是图像没有加载,我收到了错误消息:跨源资源共享策略阻止了跨域图像加载。


你能在这里发布你的POST请求吗?例如,在上传到S3时传递的策略和参数。 - Avichal Badaya
为什么使用POST请求而不是GET请求? - kateray
好的,你能提供有关获取请求的信息吗? - Avichal Badaya
这只是一个“src” - 我应该以其他方式格式化请求吗? - kateray
你能否将图片存储在S3上?这是您在检索图像时遇到的问题吗?尝试使用S3对象详细信息部分中的链接。它将类似于https://s3.amazonaws.com/<bucketname>/...,然后检查是否仍然出现错误。我已经使用CORS实现了完全相同的事情,因此如果您给我更多详细信息,我可以帮助您解决问题。 - Avichal Badaya
9个回答

177
2014年6月26日,AWS在CloudFront上发布了正确的Vary: Origin行为,因此现在您只需要:

  1. 为S3存储桶设置CORS配置,其中包括

    <AllowedOrigin>*</AllowedOrigin>

  2. 在CloudFront-> Distribution-> Behaviors for this origin中

    • 允许HTTP方法:+OPTIONS
    • 缓存HTTP方法+OPTIONS
    • 基于选定的请求标头进行缓存:将Origin标头加入白名单中。
  3. 等待约20分钟,使CloudFront传播新规则

现在,您的CloudFront分发应该会为不同的客户端Origin标头缓存不同的响应(带有正确的CORS标头)。


1
很好。这看起来也解决了通过HTTP和HTTPS提供CORS的问题。 - Ray
7
顺带一提,我还需要更改缓存行为,使其根据“允许的HTTP方法”进行变化,包括OPTIONS。 - welegan
1
你漏掉了一步。浏览器会发送一个OPTIONS请求来验证Origin头是否被允许。因此,你应该点击“GET, HEAD, OPTIONS”,而不仅仅是默认的“GET, HEAD”,确保Options没有被缓存(否则Origin将始终相同!) - Lee Benson
6
根据 AWS 关于此的文档,如果使用 S3 源并且想要缓存 OPTIONS(通常情况下应该是需要的),则还应该将 Access-Control-Request-HeadersAccess-Control-Request-Method 加入白名单。 - user
1
有时您可能需要重置本地测试浏览器中的缓存。这些修复方法对我有效,但由于缓存问题未能传播到我的浏览器 :) - MathBunny
显示剩余4条评论

79
为了补充@Brett的答案,AWS文档中详细介绍了在CloudFront上使用CORS在S3上使用CORS
其中详细步骤如下:
1. 进入您的S3存储桶,然后进入权限 -> CORS配置。 2. 在编辑器中添加CORS规则,<AllowedOrigin>规则是关键。保存配置。 enter image description here 3. 进入您的CloudFront分布式内容,然后转到行为 ->选择一个行为 ->编辑。 4. 根据您是否希望OPTIONS响应被缓存,AWS有两种方法:
如果您希望缓存OPTIONS响应,请执行以下操作:
- 选择默认缓存行为设置的选项,以启用对OPTIONS响应的缓存。 - 配置CloudFront转发以下标头:Origin、Access-Control-Request-Headers和Access-Control-Request-Method。
如果您不想缓存OPTIONS响应,请配置CloudFront转发Origin标头以及您的源需要的任何其他标头。

enter image description here

这样,使用CloudFront和S3的CORS应该可以正常工作。


30
何时应该缓存OPTIONS响应,何时不应该缓存? - Fabien Snauwaert
1
这应该可以工作,即使存储桶策略只给予CloudFront分发读取权限,对吗?我之所以提到这一点,是因为您在图像中强调了公共权限。 - ChrisOdney
哇,非常感谢你的回答。我们在过去几个月中一直错误地运行了我们的云前端。非常感谢你! - Capaj
在测试更改是否生效时,请记得发送一个带有“Origin:<某个主机>”头的请求,否则您将无法收到新的头信息。 - Kurt

38

2022答案:

  1. 进入您的S3存储桶 -> 权限
  2. 向下滚动至跨源资源共享(CORS)
  3. 应用策略:
[
    {
        "AllowedHeaders": [],
        "AllowedMethods": [
            "GET"
        ],
        "AllowedOrigins": [
            "*"
        ],
        "ExposeHeaders": []
    }
]

这将允许所有来源的GET请求。根据您的项目需求进行修改。

  1. 进入您的CloudFront分发 -> 行为 -> 编辑(在我的情况下,我只有一个行为)

  2. 向下滚动到缓存键和源请求

  3. 选择缓存策略和源请求策略(推荐)

  4. 源请求策略-可选下选择CORS-CustomOrigin

  5. 保存更改

完成!


1
谢谢,谢谢,谢谢。我已经浪费了一天的时间在不同的解决方案上。https://www.youtube.com/watch?v=KIltfPRpTi4&t=2非常好,但UI已过时,而且没有涉及到我的具体情况,即字体文件被阻止的情况。 - Steven Grant
1
@StevenGrant 我也浪费了一天的时间 :) - Nikolay Dyankov
在“缓存策略”中,我选择哪个选项? - George Henrique
4
为什么使用CORS-CustomOrigin而不是CORS-S3Origin - Jarad
对我来说很有效!谢谢 - Eyal Solomon
你推荐使用什么缓存策略?CachingOptimized(推荐用于S3)?另外,你能解释一下为什么要使用CORS-CustomOrigin而不是CORS-S3Origin吗? - undefined

14

2023年3月 - 这对我有效。

S3 Bucket配置

跨域资源共享(CORS)策略适用于S3存储桶(存储桶详细信息>权限)。根据您的项目调整AllowedOriginsAllowedHeaders(在设置测试期间可以使用*)。

[
    {
        "AllowedHeaders": [
            "*"
        ],
        "AllowedMethods": [
            "GET",
            "HEAD"
        ],
        "AllowedOrigins": [
            "*"
        ],
        "ExposeHeaders": []
    }
]

AWS Console for bucket CORS

Cloudfront CDN 配置

前往 Cloudfront 分发 > 编辑行为(在大多数情况下,每个分发只有一个)

CDN Behavior config


谢谢。这确实非常有用。有一件事让我遇到了困扰:为了使CloudFront发出我想要的Access-Control-Allow-Origin: *响应头,您必须在请求中包括一个Origin头。如果没有它,CloudFront只会额外发出Vary: Origin - Bellarmine Head
2
原始请求策略Core-CustomOrigin对我来说不起作用,如果您的CloudFront与S3连接,则选择CORS-S3Origin会起作用。对我来说,更改立即生效。 - Gulam Hussain
@GulamHussain 我也是这样的 - undefined

7

更新:随着CloudFront的最新更改,这已不再成立。太好了!有关详细信息,请参见其他响应。我保留这个问题的历史和背景。

问题

CloudFront不完全支持CORS。问题在于CloudFront如何缓存请求的响应。之后对同一URL的任何其他请求都将导致缓存请求被使用,无论其来源为何。其中关键部分是它包括来自源的响应头。

在CloudFront未缓存任何内容之前,从Origin: http://example.com发出的第一个请求的响应头如下:

Access-Control-Allow-Origin: http://example.com

第二个来自 Origin: https://example.com 的请求(注意是HTTPS而不是HTTP)也具有以下响应头:

Access-Control-Allow-Origin: http://example.com

因为这就是CloudFront缓存的URL。这是无效的 - 浏览器控制台(至少在Chrome中)将显示CORS违规消息,事情就会破裂。 解决方案 建议的解决方案是为不同的来源使用不同的URL。诀窍是附加一个唯一的查询字符串,使得每个来源都有一个已缓存的记录。
所以我们的URL将类似于:
http://.../some.png?http_mysite.com
https://.../some.png?https_mysite.com

这种方法有一定效果,但是任何人都可以通过交换查询字符串来使您的网站工作不良。这种情况可能会发生吗?可能不会,但调试此问题非常麻烦。

正确的解决方法是在CloudFront完全支持CORS之前不要使用它进行CORS。

实践中

如果您使用CloudFront进行CORS,请备用其他方法,以在CORS无法正常工作时使用。这并不总是一个选项,但现在我正在使用JavaScript动态加载字体。如果基于CORS的请求到CloudFront失败,我会退回到服务器端代理字体(非跨域)。这样,即使CloudFront某些文件存在错误缓存记录,事情仍将正常工作。


3
这可能会有所帮助:http://ryanwood.com/2014/09/03/a-chrome-update-breaks-cdn-fonts/。 - rassom

6

谢谢!那个步骤真的很有用。特别是curl命令,您可以使用它来测试是否已正确配置: curl -H“origin:example.com” -v“https://www.anything.net/video/call/System.generateId.dwr” - checketts

5

分享一些我进行的非常规配置,以使其工作:

  1. 为CloudFront分配自定义域名,使得自定义域名成为您的应用程序前端运行的子域。在OP的情况下,他使用localhost:3000;很可能他正在测试开发环境,但他必须将此应用程序部署在某个域上:让我们称之为“myapp.com”。因此,他可以分配一个自定义域,比如cdn.myapp.com指向blah.cloudfront.net。您需要为新的自定义域创建/导入自定义SSL证书;默认的CloudFront证书不起作用。

  2. 请参阅此链接:https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/CNAMEs.html enter image description here

enter image description here 在CloudFront Distribution快照中,第一行没有自定义域,因此CNAMEs列为空。第二行有一个自定义域,因此我们在那里打印了它。您可以通过这种方式验证您的自定义域是否指向了CloudFront分配。

  1. Cloudfront行为:我假设您已经设置了受信任的密钥组,因此在此时,您已经拥有签名cookie。然而,您需要创建自定义缓存策略和来源请求策略。enter image description here请参见以下自定义缓存策略的截图:enter image description here和来源请求策略:enter image description here需要注意的是,您需要将这些标头列入白名单:Origin、Access-Control-Request-Method、Access-Control-Allow-Origin、Access-Control-Request-Headers。(您可能会注意到Access-Control-Allow-Origin不在下拉列表中;只需输入即可!)还要允许所有cookie。

  2. S3 CORS配置:转到S3存储桶并单击权限选项卡。向下滚动到CORS配置。免责声明:我只是复制了对我有效的内容。这样做的原因是,在我的情况下,这个S3将被CDN或应用程序访问。我尝试放宽了要求,使用了“*”字符,但Chrome上的CORS策略会抱怨我不能在AllowedOrigins中使用通配符条目!

[
    {
        "AllowedHeaders": [
            "*"
        ],
        "AllowedMethods": [
            "PUT",
            "POST",
            "GET",
            "HEAD",
            "DELETE"
        ],
        "AllowedOrigins": [
            "cdn.myapp.com",
            "myapp.com",
            "https://cdn.myapp.com",
            "https://myapp.com"
        ],
        "ExposeHeaders": [
            "ETag"
        ]
    }
]
  1. react-player: 我是这样使用react-player的 (注意forceHLS选项被设置了,但它只是针对我的使用情况。一般情况下,我认为这并不是必需的)
<ReactPlayer
    className="react-player"
    url={url}
    controls={controls}
    light={light}
    config={
      {
        file: {
          forceHLS: true,
          hlsOptions: {
            xhrSetup: function (xhr, url) {
              xhr.withCredentials = true; // send cookies
            },
          },
        },
      }
    }
    playIcon={<PlayIcon />}
    width="100%"
    height="100%"
  />

5

我遵循了AWS文档:

然后我使用aws cdk为我完成。完整源代码在这里:https://github.com/quincycs/quincymitchell.com

const myBucket = new Bucket(this, 'bucket', {
  bucketName: `prod-${domainName}`,
  cors: [{
    allowedMethods: [HttpMethods.GET],
    allowedOrigins: ['*'],
    allowedHeaders: ['*']
  }],
  enforceSSL: true,
  blockPublicAccess: BlockPublicAccess.BLOCK_ALL,
  removalPolicy: RemovalPolicy.RETAIN
});
const mycert = Certificate.fromCertificateArn(this, 'certificate', ssmCertArn);

new Distribution(this, 'myDist', {
  defaultBehavior: {
    origin: new S3Origin(myBucket),
    viewerProtocolPolicy: ViewerProtocolPolicy.REDIRECT_TO_HTTPS,
    originRequestPolicy: OriginRequestPolicy.CORS_S3_ORIGIN,
    responseHeadersPolicy: ResponseHeadersPolicy.CORS_ALLOW_ALL_ORIGINS,
    allowedMethods: AllowedMethods.ALLOW_GET_HEAD_OPTIONS, // needed for cors
    cachedMethods: CachedMethods.CACHE_GET_HEAD_OPTIONS, // needed for cors
  },
  defaultRootObject: 'index.html',
  domainNames: [domainName, `www.${domainName}`],
  certificate: mycert
});

谢谢您。我已经拥有了一切,但是缺少了 allowedMethods: AllowedMethods.ALLOW_GET_HEAD_OPTIONS 部分。现在这对我来说完美地运作了。 - JoshStrange

0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接