【问题标题】:Pre-Signed S3 URL Signature Does Not Match预签名的 S3 URL 签名不匹配
【发布时间】:2014-09-01 07:48:19
【问题描述】:

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

我正在对文件使用 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'
}

我错过了什么?

【问题讨论】:

    标签: node.js amazon-web-services amazon-s3 pre-signed-url


    【解决方案1】:

    我在使用服务器端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
    }
    

    不同的是,上面创建的签名不包括内容类型,其中请求确实指定了内容类型。 params需要匹配headers,否则抛出的错误会是签名不匹配。

    成功示例如下:

    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' };(不带-
    • @SvenDukat 已修复
    【解决方案2】:

    试试这个。您需要上传对象,然后针对现有对象生成签名的 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);
                }
            });
        }
    });
    

    【讨论】: