我正在为一项服务编写客户端,该服务提供上传的签名网址。这适用于较小的上传,但对于使用分段上传而受益的较大上传则无效。
授权文档建议我可以在URL或Authorization标头中使用提供的签名和访问密钥ID。我已经尝试使用标头方法来启动分段上传,但是我拒绝访问。当我使用查询字符串方法时,我得到一个不允许的方法(在这种情况下为POST)。
我正在使用boto生成网址。例如:
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会有些痛苦。
相反,我发现可以创建一个联合令牌,以提供对存储桶中特定密钥的读/写访问权限。这最终变得更加实用和简单,因为我可以立即使用boto,就像我有凭据一样。
答案 0 :(得分:3)
以下是为下一个想要这样做的人节省时间的代码:
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()
答案 1 :(得分:1)
要回答我自己的问题,最好的选择是使用安全令牌服务生成一组临时凭证。可以提供策略描述以将凭证限制为特定存储桶和密钥。