预签名的S3 URL签名不匹配。

20

标题说明了一切。这是我的代码;

我正在使用node-formidable来处理文件。

form.on("end",function(field, file){
        params.Body = fs.createReadStream(params.filePath)
        delete params.filePath;
        S3.getSignedUrl('putObject',params, function(err, url) {
            if(err){console.log(err);}
            console.log(url);
        });
    })

成功上传后,url变量将返回S3的URL,类似于:

https://bucket-name.s3.amazonaws.com/746575308_8c081369df.jpg?AWSAccessKeyId=[key]&Expires=[date]&Signature=[signature]&x-amz-acl=public-read

但仍然收到“SignatureDoesNotMatch”错误。报错描述如下:

我们计算的请求签名与您提供的签名不匹配。请检查您的密钥和签名方法。

以下是我的参数:

params = {
    Bucket:"bucketname",
    Key: file.name,
    ACL: 'public-read'
}

我错过了什么?

3个回答

26

在使用S3.getSignedUrl('putObject'进行服务器端签名后,我遇到了同样的问题,并尝试在客户端使用该URL。

在我的情况下,我注意到一个可能与你有关的问题是:所有S3.getSignedUrl创建的签名都考虑了请求标头。 因此,如果您生成URL,则除非使用相同的标头发送,否则将出现与您接收到的相同错误消息。

一个失败的示例:像这样生成...

var params = { Bucket: 'YourBucket', Key: 'uniqueFileKey', Expires: 10000 };
s3.getSignedUrl('putObject', params, function (err, url) {
      if(err){
        return cb(err);
      }
      return cb(null, url)
    });

当使用生成的相同url时,以下请求在浏览器中失败。

RequestMethod: Put
Headers: {
    Accept:*/*
    Accept-Encoding:gzip, deflate, br
    Accept-Language:en-US,en;q=0.9
    Connection:keep-alive
    Content-Length:11768
    Content-Type:application/x-www-form-urlencoded; charset=UTF-8
}

区别在于上面创建的签名不包括 content-type,而请求确实指定了 content-type。参数需要与头部匹配,否则会抛出“签名不匹配”的错误。

以下是成功的示例:

var params = { Bucket: 'YourBucket', Key: 'uniqueFileKey', Expires: 10000, ContentType: 'application/x-www-form-urlencoded; charset=UTF-8' };
s3.getSignedUrl('putObject', params, function (err, url) {
      if(err){
        return cb(err);
      }
      return cb(null, url)
    });

@farrellw:小笔误,成功示例中的代码应该是: var params = { Bucket: 'YourBucket', Key: 'uniqueFileKey', Expires: 10000, ContentType: 'application/x-www-form-urlencoded; charset=UTF-8' }; (没有“-”) - Sven Dukat
@SvenDukat 已修复。 - jellycsc
4
即使你正确地设置了内容类型,如果你使用浏览器发送POST请求而不是PUT请求,你仍然会收到这个错误(就像我刚刚经历的那样,在敲了两个小时后终于发现了问题所在)。 - Andy

2

我遇到了同样的问题。farrellw的答案解释了原因。

在您的情况下,您应该在客户端请求中添加标题x-amz-acl和值public-read。因为您在服务器上预签名时指定了"ACL"为"public-read"。

您可以参考https://aboutweb.dev/blog/signaturedoesnotmatch-s3-direct-upload/获取更多详细信息。


-5

试一下这个。你需要上传对象,然后针对现有对象生成签名 URL。

var s3bucket = 'somebucket';
var s3Key = '/some/key',
var body = fs.createReadStream('/some/local/file.txt');

var params = {
    Bucket: s3bucket,
    Key: s3Key,
    Body: body
};
s3.upload(params, function(err) {
    if (err) {
        cb_1(err);
    } else {
        var params = {
            Bucket: s3bucket,
            Key: s3Key,
            Expires: parseInt(ttl)
        };
        s3.getSignedUrl('getObject', params, function(err, url) {
            if (err) {
                console.log(err);
            } else {
                console.log(err);
            }
        });
    }
});

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