S3 拒绝带有预签名 URL 的浏览器 PUT - 它认为它们是 GET

S3 rejects browser PUT's with presigned URLs - it thinks they're GET's

我正在尝试使用预签名 URL 和 PUT 方法将文件从浏览器(Chrome 版本 41.0)上传到 S3。我在我的服务器上正确生成预签名 URL,因为它在 CURL:

中运行良好

curl -X PUT "Content-Type:" --data "junk" "https://mypresignedurl"

我现在正在尝试使用 jQuery 从浏览器上传:

$.ajax({ url: "https://mypresignedurl", method: 'PUT', // I've also tried type: 'PUT' data: "junk", })

这被 AWS 使用 SignatureDoesNotMatch 代码拒绝。错误指定:

<StringToSign>GET 1427216999 /mybucket/myfile</StringToSign>

但我没有使用 GET - 我使用的是 PUT。如果我检查 Chrome 的网络选项卡,它会正确地将请求列为 PUT。那么为什么 AWS 将浏览器 PUT 请求解释为 GET 并拒绝它们?

(这似乎与 CORS 无关。我已经在我的存储桶上设置了 CORS,然后使用 -H "Origin: http://elsewhere.com" 从 CURL 进行了测试。我也试过通过"type" 而不是 "method"。我尝试使用 POST 而不是 PUT - 即使那样 AWS 仍然将 StringToSign 列为 GET。我尝试了各种 contentTypes。FWIW,我的预签名URL 在 WPF 应用程序中,上传逻辑在 .NET 中工作正常。)

好的,成功了。我现在可以使用预签名 URLs.

从浏览器上传

让我失望的是 S3 将我的 PUT 解释为 GET 的问题。但这实际上是错误的。为了找出权限问题,我查看了 Chrome 的网络选项卡。在对 S3 的 PUT 请求之前,有一个对同一个 S3 端点的 OPTIONS 请求。我单击此按钮以获取更多信息,那时我看到了带有 <StringToSign>GET ... </StringToSign>SignatureDoesNotMatch 消息 但是在浏览器中单击此 link 会导致浏览器尝试从 S3 获取资源。这不是你应该用它做的。正在使用 OPTIONS 方法检索请求。如果您确实愿意,可以使用 curl -X OPTIONS ... 手动执行此操作,然后它会正确列出如何访问此 url。但是不要在浏览器中点击它,否则浏览器会尝试获取它,产生一条与您的原始 PUT 请求无关的无意义消息。

那么,为什么我一开始就遇到上传错误?原来我把 content-type 留空了。浏览器显然不喜欢这样,把它变成了" , text/plain ..."。另一方面,似乎 curl 和 .net 可以使用空白 content-type,因此它们的行为存在差异。当我在服务器上和 ajax 上传中使用相同的 content-type = "text/plain" 生成预签名 URL 时,一切正常。