尝试使用签名方法将文件上传到S3时如何解决“ SignatureDoesNotMatch”错误

时间:2019-05-11 11:55:30

标签: javascript node.js amazon-s3 aws-sdk

我的node.js express应用程序当前可以通过服务器将图像文件上传到S3存储桶,而不会出现问题。 aws.s3的所有配置都很好,我可以确认这项工作。我现在正在尝试实现从浏览器直接上传到S3存储桶的过程,并且在实现过程中遇到了一些问题(例如CORS等),我已经解决了所有这些问题。不幸的是,我似乎无法解决(我希望这是最终的问题),“我们计算出的请求签名与您提供的签名不匹配。请检查您的密钥和签名方法。”,错误代码“ SignatureDoesNotMatch”。

我仔细检查了我能找到的所有StackOverflow和Google结果,并尝试了建议的每个选项(在此过多提及),但无济于事。我相信我的代码尽可能干净和正确,但是希望您能获得解决此问题的任何指导。在本地主机上运行和运行在Heroku上托管的已部署网站时,此操作均失败。提醒一下,通过服务器上传的图像在两种情况下均能正常工作。

让我们从存储桶上的CORs配置开始:

<?xml version="1.0" encoding="UTF-8"?>
<CORSConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
<CORSRule>
    <AllowedOrigin>*</AllowedOrigin>
    <AllowedMethod>GET</AllowedMethod>
    <AllowedMethod>POST</AllowedMethod>
    <AllowedMethod>PUT</AllowedMethod>
    <AllowedMethod>HEAD</AllowedMethod>
    <MaxAgeSeconds>3000</MaxAgeSeconds>
    <AllowedHeader>*</AllowedHeader>
</CORSRule>
</CORSConfiguration>

要获取signedUrl的服务器端代码:

const aws = require('aws-sdk');

const AWS_S3_BUCKET = process.env.AWS_S3_BUCKET;

aws.config.update({
  secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
  accessKeyId: process.env.AWS_ACCESS_KEY_ID,
  region: 'eu-west-2'
});

const s3 = new aws.S3({signatureVersion: 'v4'});

exports.getSignS3 = (req, res, next) => {
  const fileName = req.query.filename;
  const fileType = req.query.filetype;

  const s3Params = {
    Bucket: AWS_S3_BUCKET,
    Key: fileName,
    Expires: 60,
    ContentType: fileType,
    ACL: 'public-read'
  };

  s3.getSignedUrl('putObject', s3Params, (err, data) => {
    if(err){
      return res.end();
    }
    const returnData = {
      signedRequest: data,
      url: `https://${AWS_S3_BUCKET}.s3.eu-west-2.amazonaws.com/${fileName}`
    };
    res.status(200).json(returnData);
  });
};

最后是客户端脚本:

  document.getElementById("file-input").onchange = () => {
    const files = document.getElementById('file-input').files;
    const file = files[0];
    if(file == null){
      return alert('No file selected.');
    }
    getSignedRequest(file);
  };

  function getSignedRequest(file){
    const xhr = new XMLHttpRequest();
    xhr.open('GET', `/image/sign-s3?filename=${encodeURIComponent(file.name)}&filetype=${file.type}`);
    xhr.onreadystatechange = () => {
      if(xhr.readyState === 4){
        if(xhr.status === 200){
          const response = JSON.parse(xhr.responseText);
          uploadFile(file, response.signedRequest, response.url);
        }
        else{
          alert('Could not get signed URL.');
        }
      }
    };
    xhr.send();
  }

  function uploadFile(file, signedRequest, url){
    const xhr = new XMLHttpRequest();
    xhr.open('PUT', signedRequest);
    xhr.onreadystatechange = () => {
      if(xhr.readyState === 4){
        if(xhr.status === 200){
          document.getElementById('preview').src = url;
          document.getElementById('avatar-url').value = url;
        }
        else{
          alert('Could not upload file.');
        }
      }
    };
    xhr.send(file);
  }

预期结果是,将有一个GET请求转到服务器代码以调用AWS并获取签名的URL。然后,使用signedUrl向AWS发出一个OPTIONS请求,以返回有效选项。然后最后应该有一个PUT请求,使用signedUrl将映像推送到AWS。不幸的是,这失败了。

响应如下。我已经手动替换了我的访问密钥,并且存储区名称是以下片段。但是请确认这两个都是正确的。

<Error>
  <Code>SignatureDoesNotMatch</Code>
  <Message>
    The request signature we calculated does not match the signature you provided. Check your key and signing method.
  </Message>
  <AWSAccessKeyId>{myIamAwsAccessKeyId}</AWSAccessKeyId>
  <StringToSign>
    AWS4-HMAC-SHA256 20190511T105642Z 20190511/eu-west-2/s3/aws4_request 4046216f89b127260feadd407160c8e2fc3dd3a5d19096850c7e292f1c884bbf
  </StringToSign>
  <SignatureProvided>
    f787cc83f3d4152d9682e5670ba725b65e095696649ca2f1eafb0da3513af93e
  </SignatureProvided>
  <StringToSignBytes>{removed for brevity}</StringToSignBytes>
  <CanonicalRequest>
    GET /IMG_5279.jpeg Content-Type=image%2Fjpeg&X-Amz-Algorithm=AWS4- 
HMAC-SHA256&X-Amz-Credential={myIamAwsAccessKeyId}%2F20190511%2Feu- 
west-2%2Fs3%2Faws4_request&X-Amz-Date=20190511T105642Z&X-Amz- 
Expires=60&X-Amz-SignedHeaders=host%3Bx-amz-acl&x-amz-acl=public- 
read host:{myBucketName}.s3.eu-west-2.amazonaws.com x-amz- 
acl:public-read host;x-amz-acl UNSIGNED-PAYLOAD
  </CanonicalRequest>
  <CanonicalRequestBytes>{removed for brevity}</CanonicalRequestBytes>
  <RequestId>05FBF12EBB2816D5</RequestId>
 <HostId>angoSNMus7AiVIjCtknMCmtTPAJdc+RJ/I7IuTgwPNFiCOa9NgcdCb938bTHkyN2bXlTxfk/5NU=</HostId>
</Error>

任何解决此问题的建议都将不胜感激。

0 个答案:

没有答案