【问题标题】:Upload to s3 with curl using pre-signed URL (getting 403)使用预签名 URL 使用 curl 上传到 s3(获取 403)
【发布时间】:2012-02-22 11:46:12
【问题描述】:

我正在使用 curl 调用 Java ReST API 以检索 URL。然后,Java 使用我的 S3 凭据为 S3 上传生成预签名 URL,并在 ReST 回复中返回该 URL。 Curl 获取 URL 并将其用于上传到 S3,但 S3 返回 403“我们计算的请求签名与您提供的签名不匹配。请检查您的密钥和签名方法。”

这是我用来生成预签名 URL 的代码:

public class S3Util {
    static final AmazonS3 s3 = new AmazonS3Client( new AWSCredentials() {
        @Override
        public String getAWSAccessKeyId() {
            return "XXXXXXX";
        }
        @Override
        public String getAWSSecretKey() {
            return "XXXXXXXXXXXXXX";
        }
    });
    static final String BUCKET = "XXXXXXXXXXXXXXXXXXXXXXXXXXX";

    static public URL getMediaChunkURL( MediaChunk mc, HttpMethod method ) {
        String key = ...
        //way in the future (for testing)...
        Date expiration = new Date( System.currentTimeMillis() + CalendarUtil.ONE_MINUTE_IN_MILLISECONDS*60*1000 );

        GeneratePresignedUrlRequest req = new GeneratePresignedUrlRequest(BUCKET, key, method);
        req.setExpiration(expiration);
        req.addRequestParameter("Content-Type", "application/octet-stream");

        //this gets passed to the end user:
        return s3.generatePresignedUrl(req);
    }
}

在 curl 中,从 bash 运行,我执行这个:

echo Will try to upload chunk to ${location}
curl -i -X POST \
        -F 'Content-Type=application/octet-stream' \
        -F "file=@${fileName}" \
        ${location} || (echo upload chunk failed. ; exit 1 )

除其他外,我尝试过 PUT,并且尝试过“内容类型”(小写 T)。我意识到我遗漏了一些(或一些)明显的东西,但是在阅读了适当的文档、谷歌搜索并查看了许多类似的问题之后,我不确定那是什么。我看到很多关于所需标头的提示,但我认为辞职的 URL 应该消除这些需求。也许不是?

TIA!

更新:

为了清楚起见,我已经测试了下载,效果很好。

Java 看起来像:

GeneratePresignedUrlRequest req = new GeneratePresignedUrlRequest(BUCKET, key, HttpMethod.GET);
req.setExpiration(expiration);

而 curl 很简单:

curl -i ${location}

【问题讨论】:

    标签: java bash curl amazon-s3


    【解决方案1】:

    尽管GeneratePresignedUrlRequest 接受http 方法参数(并具有setMethod 函数),但它似乎无法用于GET 之外的任何东西。

    http://wiki.nercomp.org/wiki/images/0/05/AmazonWebServices.pdf 声明“签署请求并将其交给第三方执行的做法仅适用于简单的对象 GET 请求。”也许设置另一种方法可以用于某些事情,但显然不是这个。

    因此,我不得不按照此处的说明进行操作:

    http://aws.amazon.com/articles/1434?_encoding=UTF8&jiveRedirect=1

    这更复杂,因为客户端需要发布完整的表单,而不仅仅是使用 URL,并且还意味着所有发布信息都必须单独传达给客户端,但它似乎确实有效。

    【讨论】:

    • 有趣,我不知道这个(以前的?)限制 - 虽然它已从 current documentation 关于这个主题中删除,例如PUT on S3 : Problem 也说明了一个使用 PUT 的明显有效示例(尽管在那里讨论了附带问题)。
    • 我刚刚确认通过 C# 生成预签名 URL 并随后通过 curl 下载,使用 PUT 确实可以正常工作 - 我会发布更多详细信息作为休息后的答案。
    • @SteffenOpel 我相信问题出在使用 java sdk 时生成 put 请求,更具体地说是在签名中。此返回的 url 可用于查询服务器,但您将收到一个禁止错误,以及一些用于对字节进行签名的字符串(请求),如果您使用其他任何内容对该字符串进行签名,则签名将有所不同。您甚至可以将这个新签名与请求中的签名交换,并且查询将起作用。也许这是一个 java 错误
    【解决方案2】:

    我已经能够通过 C# 生成一个预签名的 URL,然后按预期通过curl 上传它。鉴于我的测试,我怀疑您确实没有正确使用 curl - 我已经能够像这样上传文件:

    curl -v --upload-file ${fileName} ${location}
    

    参数-v 转储请求和响应标头(以及 SSL 握手)用于调试和说明目的:

    > PUT [...] HTTP/1.1
    > User-Agent: curl/7.21.0 [...]
    > Host: [...]
    > Accept: */*
    > Content-Length: 12
    > Expect: 100-continue
    

    请注意,--upload-file(或-T)按预期促进PUT,但会酌情添加更多标头,从而产生适当的响应:

    < HTTP/1.1 100 Continue
    < HTTP/1.1 200 OK
    < x-amz-id-2: [...]
    < x-amz-request-id:  [...]
    < Date: Tue, 31 Jan 2012 18:34:56 GMT
    < ETag: "253801c0d260f076b0d5db5b62c54824"
    < Content-Length: 0
    < Server: AmazonS3
    

    【讨论】:

    • 谢谢。我坚持我的解决方案,因为我首先让它工作,但我将其标记为正确,因为它解决了原始查询,老实说,我认为它更好。
    • 对我来说,预签名的 url 包含需要我用引号括住整个 url 的字符。一旦我这样做了,你的答案就完美了。
    • 对我来说,使用带有 POST 的预签名 URL 是行不通的。仅切换方法(在 GeneratePresignedUrlRequest 和 curl 中)解决了问题。
    【解决方案3】:

    使用 curl 执行此操作时,您需要将 url 放在单引号中,否则查询字符串的一半会被截断(带有密钥/签名的部分)。

    【讨论】:

      【解决方案4】:

      生成网址的方式:

      private static URL generateRUL(String objectKey, String ACCESS_KEY, String SECRET_KEY, String BUCKET_NAME) {
          AmazonS3 s3Client = new AmazonS3Client(new BasicAWSCredentials(ACCESS_KEY, SECRET_KEY));
          URL url = null;
      
          try {
              GeneratePresignedUrlRequest request  = new GeneratePresignedUrlRequest(BUCKET_NAME, objectKey);
              request.setMethod(com.amazonaws.HttpMethod.PUT);
              request.setExpiration(new Date( System.currentTimeMillis() + (60 * 60 * 1000)));
      
              // Very important ! It won't work without adding this! 
              // And request.addRequestParameter("Content-Type", "application/octet-stream") won't work neither
              request.setContentType("application/octet-stream");
      
              url = s3Client.generatePresignedUrl(request ); 
          } catch (AmazonServiceException exception) { 
          } catch (AmazonClientException ace) { }
      
          return url;
      }
      

      文件上传方式:

      public int upload(byte[] fileBytes, URL url) {
          HttpURLConnection connection = (HttpURLConnection) url.openConnection();
          connection.setDoOutput(true);
          connection.setRequestMethod("PUT");
          connection.setRequestProperty("Content-Type", "application/octet-stream"); // Very important ! It won't work without adding this!
          OutputStream output = connection.getOutputStream();
      
          InputStream input = new ByteArrayInputStream(fileBytes);
          byte[] buffer = new byte[4096];
          int length;
          while ((length = input.read(buffer)) > 0) {
              output.write(buffer, 0, length);
          }
          output.flush();
      
          return connection.getResponseCode();
      }
      

      【讨论】:

      • request.setContentType("application/octet-stream");有用!我之前收到 403 禁止错误。我想知道它为什么起作用
      猜你喜欢
      • 2021-10-03
      • 1970-01-01
      • 2021-08-26
      • 1970-01-01
      • 2016-10-12
      • 2020-09-30
      • 1970-01-01
      • 2018-07-08
      • 2021-02-15
      相关资源
      最近更新 更多