【问题标题】:Amazon S3 Signature Does Not Match - AWS SDK JavaAmazon S3 签名不匹配 - AWS 开发工具包 Java
【发布时间】:2014-01-09 16:12:14
【问题描述】:

我有一个需要将文件上传到 S3 的播放应用程序。我们正在 scala 中开发并使用 Java AWS SDK。

我在尝试上传文件时遇到问题,我在使用预签名网址时不断收到 403 SignatureDoesNotMatch。该 url 是通过以下代码使用 AWS Java SDK 生成的:

def generatePresignedPutRequest(filename: String) = {
    val expiration = new java.util.Date();
    var msec = expiration.getTime() + 1000 * 60 * 60; // Add 1 hour.
    expiration.setTime(msec);

    s3 match {
      case Some(s3) => s3.generatePresignedUrl(bucketname, filename, expiration, HttpMethod.PUT).toString
      case None => {
        Logger.warn("S3 is not availiable. Cannot generate PUT request.")
        "URL not availiable"
      }
    }
  }

对于我们遵循ioncannon article的前端代码。

上传文件的js函数(同文中使用的那个)

 function uploadToS3(file, url)
     {
       var xhr = createCORSRequest('PUT', url);
       if (!xhr) 
       {
         setProgress(0, 'CORS not supported');
       }
       else
       {
         xhr.onload = function() 
         {
           if(xhr.status == 200)
           {
             setProgress(100, 'Upload completed.');
           }
           else
           {
             setProgress(0, 'Upload error: ' + xhr.status);
           }
         };

         xhr.onerror = function() 
         {
           setProgress(0, 'XHR error.');
         };

         xhr.upload.onprogress = function(e) 
         {
           if (e.lengthComputable) 
           {
             var percentLoaded = Math.round((e.loaded / e.total) * 100);
             setProgress(percentLoaded, percentLoaded == 100 ? 'Finalizing.' : 'Uploading.');
           }
         };

         xhr.setRequestHeader('Content-Type', 'image/png');
         xhr.setRequestHeader('x-amz-acl', 'authenticated-read');

         xhr.send(file);
       }
     }

服务器的响应是

<?xml version="1.0" encoding="UTF-8"?>
<Error><Code>SignatureDoesNotMatch</Code>
<Message>The request signature we calculated does not match the signature you provided. Check your key and signing method.</Message>
<StringToSignBytes>50 55 bla bla bla...</StringToSignBytes>
<RequestId>F7A8F1659DE5909C</RequestId>
<HostId>q+r+2T5K6mWHLKTZw0R9/jm22LyIfZFBTY8GEDznfmJwRxvaVJwPiu/hzUfuJWbW</HostId>
<StringToSign>PUT

    image/png
    1387565829
    x-amz-acl:authenticated-read
    /mybucketname/icons/f5430c16-32da-4315-837f-39a6cf9f47a1</StringToSign>
<AWSAccessKeyId>myaccesskey</AWSAccessKeyId></Error>

我已配置 CORS,仔细检查了 aws 凭据并尝试更改请求标头。我总是得到相同的结果。 为什么亚马逊告诉我签名不匹配?

【问题讨论】:

    标签: javascript amazon-web-services amazon-s3


    【解决方案1】:

    怀疑 OP 对此仍有问题,但对于遇到此问题的其他人,答案如下:

    向 S3 发出签名请求时,AWS 会检查以确保签名与浏览器发送的 HTTP 标头信息完全匹配。不幸的是,这是必读的:http://s3.amazonaws.com/doc/s3-developer-guide/RESTAuthentication.html

    然而,在上面的代码中,实际情况并非如此,Javascript 正在发送:

    xhr.setRequestHeader('Content-Type', 'image/png');
    xhr.setRequestHeader('x-amz-acl', 'authenticated-read');
    

    但在 Java/Scala 中,调用 s3.generatePresignedUrl 时没有传入其中任何一个。因此,生成的签名实际上是告诉 S3 拒绝任何带有 Content-Type 或 x-ams-acl 标头集的内容。哎呀(我也上当了)。

    我已经看到浏览器自动发送 Content-Types,因此即使它们没有显式添加到标头中,它们仍然可以进入 S3。那么问题来了,我们如何在签名中添加 Content-Type 和 x-amz-acl 头呢?

    AWS SDK 中有几个重载的 generatePresignedUrl 函数,但其​​中只有一个允许我们传入除存储桶名称、文件名、到期日期和 http 方法之外的任何其他内容。

    解决办法是:

    1. 使用您的存储桶和文件名创建一个 GeneratePresignedUrlRequest 对象。
    2. 调用 setExpiration、setContentType 等,在其上设置您的所有标头信息。
    3. 将其作为唯一参数传递到 s3.generatePresignedUrl。

    下面是正确的 GeneratePresignedUrlRequest 函数定义:

    http://docs.aws.amazon.com/AWSJavaSDK/latest/javadoc/com/amazonaws/services/s3/AmazonS3Client.html#generatePresignedUrl(com.amazonaws.services.s3.model.GeneratePresignedUrlRequest)

    AWS GitHub 存储库上的函数代码也有助于我了解如何编写解决方案。希望这会有所帮助。

    【讨论】:

    • 好答案!注意:如果您想为来自 S3 的响应设置 Content-Type 标头(即当 AWS 将响应发送回客户端时),您必须创建一个新的 ResponseHeaderOverrides 实例,在 that,然后在 GeneratePresignedUrlRequest 实例上调用 setRequestHeaders(RequestHeaderOverrides)。在 GeneratePresignedUrlRequest 实例上调用 setContentType 会产生上述问题的另一个变体——S3 接收到的 HTTP 请求与预签名请求具有不同的标头。
    • @EricR.Rath 您的解决方案非常有效,谢谢!
    【解决方案2】:

    我刚刚在使用 NodeJs AWS 开发工具包时遇到了这个问题。 这是由于使用了有效的凭据,但没有足够的权限。 更改为我的管理员密钥可以解决此问题,无需更改代码!

    【讨论】:

    • 我也收到了无效凭据。使用了错误的密钥。
    • 感谢您的这篇文章。这可能会帮助我弄清楚为什么我正在做的事情不起作用....
    【解决方案3】:

    遇到了问题,windows 上的 mime 类型将 fileType 设置为空字符串,但它不起作用。只需处理空字符串并添加一些文件类型。

    【讨论】:

      【解决方案4】:

      我遇到了同样的问题,但删除内容类型可以正常工作。特此分享完整代码。

      public class GeneratePresignedUrlAndUploadObject {
          private static final String BUCKET_NAME = "<YOUR_AWS_BUCKET_NAME>"; 
          private static final String OBJECT_KEY  = "<YOUR_AWS_KEY>";
          private static final String AWS_ACCESS_KEY = "<YOUR_AWS_ACCESS_KEY>";
          private static final String AWS_SECRET_KEY = "<YOUR_AWS_SECRET_KEY>";
      
          public static void main(String[] args) throws IOException {
              BasicAWSCredentials awsCreds = new BasicAWSCredentials(AWS_ACCESS_KEY, AWS_SECRET_KEY);
      
              AmazonS3 s3Client = AmazonS3ClientBuilder.standard().withRegion(Regions.US_EAST_1)
                      .withCredentials(new AWSStaticCredentialsProvider(awsCreds)).build();
      
              try {
                  System.out.println("Generating pre-signed URL.");
                  java.util.Date expiration = new java.util.Date();
                  long milliSeconds = expiration.getTime();
                  milliSeconds += 1000 * 60 * 60;
                  expiration.setTime(milliSeconds);
      
                  GeneratePresignedUrlRequest generatePresignedUrlRequest = 
                          new GeneratePresignedUrlRequest(BUCKET_NAME, OBJECT_KEY);
                  generatePresignedUrlRequest.setMethod(HttpMethod.PUT); 
                  generatePresignedUrlRequest.setExpiration(expiration);
                  URL url = s3Client.generatePresignedUrl(generatePresignedUrlRequest); 
      
                  UploadObject(url);
      
                  System.out.println("Pre-Signed URL = " + url.toString());
              } catch (AmazonServiceException exception) {
                  System.out.println("Caught an AmazonServiceException, " +
                          "which means your request made it " +
                          "to Amazon S3, but was rejected with an error response " +
                  "for some reason.");
                  System.out.println("Error Message: " + exception.getMessage());
                  System.out.println("HTTP  Code: "    + exception.getStatusCode());
                  System.out.println("AWS Error Code:" + exception.getErrorCode());
                  System.out.println("Error Type:    " + exception.getErrorType());
                  System.out.println("Request ID:    " + exception.getRequestId());
              } catch (AmazonClientException ace) {
                  System.out.println("Caught an AmazonClientException, " +
                          "which means the client encountered " +
                          "an internal error while trying to communicate" +
                          " with S3, " +
                  "such as not being able to access the network.");
                  System.out.println("Error Message: " + ace.getMessage());
              }
          }
      
          public static void UploadObject(URL url) throws IOException
          {
              HttpURLConnection connection=(HttpURLConnection) url.openConnection();
              connection.setDoOutput(true);
              connection.setRequestMethod("PUT");
              OutputStreamWriter out = new OutputStreamWriter(
                      connection.getOutputStream());
              out.write("This text uploaded as object.");
              out.close();
              int responseCode = connection.getResponseCode();
              System.out.println("Service returned response code " + responseCode);
      
          }
      }
      

      【讨论】:

        【解决方案5】:

        我遇到了类似的问题,设置配置 signatureVersion: 'v4' 在我的情况下帮助解决了它 -

        在 JavaScript 中:

        var s3 = new AWS.S3({
          signatureVersion: 'v4'
        });
        

        改编自https://github.com/aws/aws-sdk-js/issues/902#issuecomment-184872976

        【讨论】:

          【解决方案6】:

          我在使用Java AWS SDK 时遇到了SignatureDoesNotMatch 错误。在我的情况下,SignatureDoesNotMatch 错误发生在升级 maven 依赖项后,我的代码没有更改(因此凭据是正确的并且没有更改)。将依赖org.apache.httpcomponents:httpclient从版本4.5.6升级到4.5.7后(实际上是Spring Boot从2.1.2升级到2.1.3,并且bom指定了httpclient版本),代码变成了抛出异常在执行一些 AWS SDK S3 请求时,例如 AmazonS3.getObject。

          在深入挖掘根本原因后,我发现httpclient 库对标准化 URI 进行了重大更改,这影响了 Java AWS SDK S3。请查看已打开的 GitHub 票证org.apache.httpcomponents:httpclient:4.5.7 breaks fetching S3 objects 了解更多详情。

          【讨论】:

            【解决方案7】:

            对我来说同样的问题,但原因不同。我使用的是 POST 而不是 PUT

            【讨论】:

              【解决方案8】:

              如果您的访问密钥和密钥是好的,但上面写着“SignatureDoesNotMAtch”,请检查您的密钥,它可能有一些特殊字符,例如 +/ - / *

              转到 aws 并生成另一个访问密钥,其中密钥没有这些。然后再试一次:)

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 2015-04-15
                • 1970-01-01
                • 2011-11-10
                • 1970-01-01
                • 2023-03-15
                • 2014-09-20
                • 1970-01-01
                相关资源
                最近更新 更多