【问题标题】:Is there a way to use an AWS signed URL to perform a multipart upload?有没有办法使用 AWS 签名的 URL 来执行分段上传?
【发布时间】:2018-04-10 16:35:42
【问题描述】:

我正在为一项服务编写一个客户端,该服务提供一个签名的 url 用于上传。这适用于较小的上传,但对于可以从使用分段上传中受益的较大上传则失败。

授权文档建议我可以在 URL 中或通过 Authorization 标头使用提供的签名和访问密钥 ID。我已经尝试使用标头方法开始分段上传,但访问被拒绝。当我使用查询字符串方法时,我得到一个不允许的方法(在这种情况下为 POST)。

我正在使用 boto 来生成 URL。例如:

import boto

c = boto.connect_s3()
bucket = c.get_bucket('my-bucket')
key = boto.s3.key.Key(bucket, 'my-big-file.gz')
signed_url = key.generate_url(60 * 60, 'POST')  # expires in an hour

然后,当尝试使用签名 URL 开始分段上传时,我正在执行以下操作:

import requests

url = signed_url + '&uploads'
resp = requests.post(url)

这会返回一个不允许的方法。

这种策略可行吗?有没有更好的方法来为特定资源提供有限的凭据以允许大型分段上传?

更新

我设法找到了一个稍微更具体的错误,这让我认为这是不可能的。不幸的是,我收到 403 表示签名与请求不匹配。

<?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>.../StringToSignBytes>
  <RequestId>...</RequestId>
  <HostId>...</HostId>
  <SignatureProvided>...</SignatureProvided>
  <StringToSign>POST


  1402941975
  /my-sandbox/test-mp-upload.txt?uploads</StringToSign>
  <AWSAccessKeyId>...</AWSAccessKeyId>
</Error>

这让我觉得我将无法使用签名的 URL,因为签名不匹配。

更新

我认为使用签名 URL 进行分段上传是不合理的。虽然我怀疑这在技术上是可行的,但它并不实用。原因是签名的 URL 要求 URL、标头和请求方法都完全匹配才能按预期工作。由于分段上传需要初始化上传,上传每个部分并完成(或取消)上传,因此为每个步骤生成 URL 会有些痛苦。

相反,我发现可以创建一个联合令牌来提供对存储桶中特定键的读/写访问权限。这最终变得更加实用和简单,因为我可以立即使用 boto,就好像我有凭据一样。

【问题讨论】:

  • 我想 url 应该是这样的:POST /ObjectName?uploads ...,你有没有尝试把 upload 关键字放在键名后面?
  • @mr0re1 很好。我更新了问题,但没有任何改变。
  • 它改变响应错误,SignatureDoesNotMatch 而不是Not Allowed 这意味着无效的请求语法

标签: python amazon-web-services boto


【解决方案1】:

这里有一些代码可以为下一个想要这样做的人节省时间:

import json
from uuid import uuid4

import boto3


def get_upload_credentials_for(bucket, key, username):
    arn = 'arn:aws:s3:::%s/%s' % (bucket, key)
    policy = {"Version": "2012-10-17",
              "Statement": [{
                  "Sid": "Stmt1",
                  "Effect": "Allow",
                  "Action": ["s3:PutObject"],
                  "Resource": [arn],
              }]}
    client = boto3.client('sts')
    response = client.get_federation_token(
        Name=username, Policy=json.dumps(policy))
    return response['Credentials']


def client_from_credentials(service, credentials):
    return boto3.client(
        service,
        aws_access_key_id=credentials['AccessKeyId'],
        aws_secret_access_key=credentials['SecretAccessKey'],
        aws_session_token=credentials['SessionToken'],
    )


def example():
    bucket = 'mybucket'
    filename = '/path/to/file'

    key = uuid4().hex
    print(key)

    prefix = 'tmp_upload_'
    username = prefix + key[:32 - len(prefix)]
    print(username)
    assert len(username) <= 32  # required by the AWS API

    credentials = get_upload_credentials_for(bucket, key, username)
    client = client_from_credentials('s3', credentials)
    client.upload_file(filename, bucket, key)
    client.upload_file(filename, bucket, key + 'bob')  # fails


example()

【讨论】:

  • 我认为这应该是公认的答案 - @elarson
【解决方案2】:

要回答我自己的问题,最好的选择是使用安全令牌服务来生成一组临时凭证。可以提供策略描述以将凭据限制为特定的存储桶和密钥。

【讨论】:

  • 我知道它已经有很长一段时间没有得到回答了,但是你有没有机会用这种方法成功?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-07-15
  • 2021-10-25
  • 2023-04-09
  • 1970-01-01
  • 1970-01-01
  • 2013-05-24
  • 1970-01-01
相关资源
最近更新 更多