我在 Amazon S3 上托管文件,我希望使用 预签名 url 可以访问这些文件。
对于简单的 GET 请求,这非常有效。然而,有些客户端首先执行 HEAD 请求(以检索文件大小)。由于 URL 中的签名包含 http 动词(GET 与 HEAD),因此 head 请求失败。
客户只需:
HEAD http://(bucketname).s3.amazonaws.com/filename?AWSAccessKeyId=(mykey)&Expires=(timestamp)&Signature=(sig)
GET http://(bucketname).s3.amazonaws.com/filename?AWSAccessKeyId=(mykey)&Expires=(timestamp)&Signature=(sig)
我无法更改客户端以使用不同的 url 进行 head 和 get。有没有办法让亚马逊使用接受同一资源的 HEAD 和 GET 的签名?
如果您仅为第一个字节指定
HEAD
标头,您还可以使用 GET
模拟 Range
行为。不同之处在于您收到的是 206
而不是 200
代码。其次,完整尺寸将位于 Content-Range
标题中。
curl -r 0-0 <URL>
不。
HEAD
和 GET
需要不同的签名,因为签名输入存在细微差别。
不确定您使用什么来生成预签名身份验证 URL,但我知道一些官方 AWS 开发工具包可以处理此问题,而另一些则不能还。
在朋友的帮助下,我找到了一个适合我的解决方案:在我的服务器上代理 HEAD 请求,并重定向 GET 请求。
当请求带有 HEAD 动词来获取文件信息时,我使用服务器上的 S3 代码来获取 HEAD 信息,然后我自己将其发送回请求者。
当带有 GET 动词的请求传入以获取文件本身时,我会使用预签名的 URL 进行 302 重定向。
这可以完美地处理 HEAD 和 GET,不需要预先签名。我只对实际文件的 GET 请求进行预签名。
由于您只能为
GET
或 HEAD
的 URL 进行预签名,而不能同时为两者预签名,
我们通过使用 xmlhttprequest
解决了这个问题。
我们用它来启动获取请求,获取状态/标头并中止请求。
const XMLHttpRequest = require("xmlhttprequest").XMLHttpRequest;
async function getUrlStatus(url, timeout=1000){
const start = Date.now();
const xhr = new XMLHttpRequest();
let status_code;
// Start async get call
xhr.open('GET', url, true);
// Detect status change. Refer https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/readyState
xhr.onreadystatechange = function() {
if (xhr.readyState == 2) {
console.log("Headers Received", xhr.status);
console.log(xhr.getAllResponseHeaders())
} else if (xhr.readyState == 3) {
// Update the status code
status_code = xhr.status;
xhr.abort();
console.log("Loading", status_code);
return status_code
}
}
function resolveReadyState(resolve, reject){
if (status_code == 200)
resolve(status_code);
else if (status_code == 403)
resolve(status_code);
else if (timeout && (Date.now() - start) >= timeout)
reject(new Error("Timeout"));
else
setTimeout(resolveReadyState.bind(this, resolve, reject));
}
xhr.send()
return new Promise(resolveReadyState)
}
url = "https://some_presigned_file_url"
getUrlStatus(url).then(status_code => console.log("Received State". status_code))
最简单的解决方案是使用 Cloudfront 为您的 S3 存储桶创建分配。
您可以将 Cloudfront 分配设置为仅授予使用签名 URL 的访问权限。此签名 URL 将适用于 GET 和 HEAD 请求。
对 URL 进行签名的方法有所不同,但有一些示例:https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/PrivateCFSignatureCodeAndExamples.html