对 HEAD 和 GET 动词预签名 Amazon S3 url

问题描述 投票:0回答:5

我在 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 的签名?

amazon-s3
5个回答
28
投票

如果您仅为第一个字节指定

HEAD
标头,您还可以使用
GET
模拟
Range
行为。不同之处在于您收到的是
206
而不是
200
代码。其次,完整尺寸将位于
Content-Range
标题中。

curl -r 0-0 <URL>


7
投票

不。

HEAD
GET
需要不同的签名,因为签名输入存在细微差别。

不确定您使用什么来生成预签名身份验证 URL,但我知道一些官方 AWS 开发工具包可以处理此问题,而另一些则不能


6
投票

在朋友的帮助下,我找到了一个适合我的解决方案:在我的服务器上代理 HEAD 请求,并重定向 GET 请求。

当请求带有 HEAD 动词来获取文件信息时,我使用服务器上的 S3 代码来获取 HEAD 信息,然后我自己将其发送回请求者。

当带有 GET 动词的请求传入以获取文件本身时,我会使用预签名的 URL 进行 302 重定向。

这可以完美地处理 HEAD 和 GET,不需要预先签名。我只对实际文件的 GET 请求进行预签名。


0
投票

由于您只能为

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))

0
投票

最简单的解决方案是使用 Cloudfront 为您的 S3 存储桶创建分配。

您可以将 Cloudfront 分配设置为仅授予使用签名 URL 的访问权限。此签名 URL 将适用于 GET 和 HEAD 请求。

对 URL 进行签名的方法有所不同,但有一些示例:https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/PrivateCFSignatureCodeAndExamples.html

© www.soinside.com 2019 - 2024. All rights reserved.