ruby-on-rails – 尝试生成预先指定的URL链接,以便用户可以下载Amazon S3对象,但获取无效请求

我目前正在使用
Ruby aws-sdk,版本2 gem和服务器端客户提供的加密密钥(SSE-C).我可以将对象从rails表单上传到Amazon S3,没有任何问题.

def s3
  Aws::S3::Object.new(
    bucket_name: ENV['S3_BUCKET'],
    key: 'hello',
  )
end

def upload_object
  customer_key = OpenSSL::Cipher::AES.new(256, :CBC).random_key
  customer_key_md5 = Digest::MD5.new.digest(customer_key)
  object_key = 'hello'
  options = {}
  options[:key] = object_key
  options[:sse_customer_algorithm] = 'AES256'
  options[:sse_customer_key] = customer_key
  options[:sse_customer_key_md5] = customer_key_md5
  options[:body] = 'hello world'
  options[:bucket] = ENV['S3_BUCKET']
  s3.put(options)
  test_params = {
    object_key: object_key,
    customer_key: Base64.encode64(customer_key),
    md5_key: Base64.encode64(customer_key_md5),
  }
  Test.create(test_params)
end

但是我在检索对象和生成签名的URL链接以供用户下载时遇到了一些问题.

def retrieve_object(customer_key, md5)
  options = {}
  options[:key] = 'hello
  options[:sse_customer_algorithm] = 'AES256'
  options[:sse_customer_key] = Base64.decode64(customer_key)
  options[:sse_customer_key_md5] = Base64.decode64(md5)
  options[:bucket] = ENV['S3_BUCKET']
  s3.get(options)
  url = s3.presigned_url(:get)
end

该链接已生成,但当我点击它时,它会指示我进入亚马逊页面说.

<Error>
<Code>InvalidRequest</Code>
<Message>
The object was stored using a form of Server Side Encryption. The correct   parameters must be provided to retrieve the object.
</Message>
<RequestId>93684EEBA062B1C2</RequestId>
<HostId>
OCnn5EG7ydfoKzsmEDMbqK5kOhLFpNXxVRdekfhOfnBc6s+jtPYFsKi8IZsEPcd9ConbYUHgwC8=
</HostId>
</Error>

错误消息没有帮助,因为我不确定我需要添加哪些参数.我想我可能会遗漏一些权限参数.

获取方法
http://docs.aws.amazon.com/sdkforruby/api/Aws/S3/Object.html#get-instance_method

Presigned_Url方法
http://docs.aws.amazon.com/sdkforruby/api/Aws/S3/Object.html#presigned_url-instance_method

最佳答案 生成预先签名的GET对象URL时,需要提供所有传递给Aws :: S3 :: Object#get的相同参数.

s3.get(sse_customer_algorithm: 'AES256', sse_customer_key: customer_key).body.read

这意味着您需要将相同的sse_customer_ *选项传递给#presigned_url:

url = obj.presigned_url(:get,
  sse_customer_algorithm: 'AES256',
  sse_customer_key: customer_key)

这将确保SDK在您发出最终GET请求时正确签署Amazon S3期望的标头.下一个问题是您现在负责将这些值与GET请求一起作为标头发送. Amazon S3将不接受查询字符串中的算法和键.

uri = URI.parse(url)
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
request = Net::HTTP::Get.new(uri.request_uri, {
  "x-amz-server-side-encryption-customer-algorithm" => 'AES256',
  "x-amz-server-side-encryption-customer-key" => Base64.encode64(cpk),
  "x-amz-server-side-encryption-customer-key-MD5" => Base64.encode64(OpenSSL::Digest::MD5.digest(cpk))
})

请注意 – 在测试时,我在aws-sdk gem的当前v2.0.33版本的预签名URL实现中发现了一个错误. This has been fixed now并且一旦发布就应该成为v2.0.34的一部分.

请参阅以下gitst以获取修补该bug并演示的完整示例:

>使用cpk上传对象
>使用SDK获取对象
>生成预先签名的GET URL
>仅使用Net :: HTTP和预签名URL下载对象

您可以在此处查看示例脚本:

https://gist.github.com/trevorrowe/49bfb9d59f83ad450a9e

只需替换脚本顶部的bucket_name和object_key变量即可.

点赞