我使用以下Python / Boto代码为Amazon S3存储桶生成一次性文件上传URL:
from boto.s3.connection import S3Connection
def get_signed_upload_url():
s3 = S3Connection(ACCESS_KEY_ID, SECRET_ACCESS_KEY, is_secure=True)
return s3.generate_url(300, 'PUT', bucket=BUCKET, key=KEY,
headers={'Content-Type': 'text/plain'})
它已经运行了好几年,今天仍在继续。
但是现在,我正在转换为IAM角色 - 这将使我免于对ACCESS_KEY_ID
和SECRET_ACCESS_KEY
进行硬编码。因此,我删除了硬编码密钥,产生了以下代码:
from boto.s3.connection import S3Connection
def get_signed_upload_url():
s3 = S3Connection(is_secure=True)
return s3.generate_url(300, 'PUT', bucket=BUCKET, key=KEY,
headers={'Content-Type': 'text/plain'})
使用此代码,每当我生成一个URL并从我的客户端Web应用程序(通过Ajax)向它发出PUT
请求时,我都会得到一个HTTP 400 Bad Request
来自S3的错误:
<Error>
<Code>InvalidToken</Code>
<Message>The provided token is malformed or otherwise invalid.</Message>
<!-- account-specific stuff removed -->
</Error>
为什么会这样?
一些额外的细节:
generate_url()
网址的GET
调用 - 使用自动传递到EC2实例环境的ACCESS_KEY_ID
工作正常。PUT
无效。Content-Type
标头,这就是那个标头在那里的原因。我尝试从此Boto代码和前端逻辑中删除Content-Type
标头,但问题仍然存在。答案 0 :(得分:1)
为了它的价值,我无法复制这个问题。我已经设置了一个全新的EC2实例,为其分配了一个附有AmazonS3FullAccess权限的IAM角色
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "s3:*",
"Resource": "*"
}
]
}
我能够安装的最早的Python版本是2.7.12和Boto 2.38.0
[root@ip-172-31-13-14 ~]# python -V
Python 2.7.12
[root@ip-172-31-13-14 ~]# pip freeze|grep boto
boto==2.38.0
botocore==1.5.95
然后我运行代码 app.py
from boto.s3.connection import S3Connection
def get_signed_upload_url():
BUCKET = 'test'
KEY = 'test-2'
s3 = S3Connection(is_secure=True)
return s3.generate_url(300, 'PUT', bucket=BUCKET, key=KEY,
headers={'Content-Type': 'text/plain'})
if __name__ == "__main__":
url = get_signed_upload_url()
print "curl -v -X PUT -H 'Content-Type: text/plain' -d @hello2.txt '" + url + "'"
然后运行生成的cURL命令,文件上传成功
> Host: test.s3.amazonaws.com
> User-Agent: curl/7.53.1
> Accept: */*
> Content-Type: text/plain
> Content-Length: 8585
> Expect: 100-continue
>
< HTTP/1.1 100 Continue
* We are completely uploaded and fine
< HTTP/1.1 200 OK
< x-amz-id-2: redacted
< x-amz-request-id: redacted
< Date: Tue, 19 Dec 2017 05:45:15 GMT
< x-amz-version-id: redacted
< ETag: "redacted"
< Content-Length: 0
< Server: AmazonS3
在这个阶段,我只能猜测你使用的IAM角色可能没有足够的权限来执行PUT操作?如果做不到这一点,客户端代码实际执行PUT请求的方式可能会有所不同,但这种可能性不大。
其他要检查的事项:
答案 1 :(得分:0)
生成签名网址时是权限问题。您能否提供您附加到实例的角色。
尝试在EC上显式运行此命令,以查看其确切失败的位置。
如果它不是角色问题,那么您正在使用的boto SDK肯定存在问题。它应该是2.5.1或更高版本。
您是否尝试将访问密钥和密钥设置为无?
答案 2 :(得分:0)
好吧,我花了一年多的时间才弄清楚-但我终于做到了。
问题是我的前端JavaScript在发出Ajax请求之前正在签名的URL上调用decodeURIComponent()
。
在我转换为IAM角色之前,这不是问题,因为签名的URL中没有任何有趣的字符-因此decodeURIComponent()
无效。但是IAM角色使用不同的签名URL语法:查询字符串包含许多斜杠,这些斜杠已被decodeURIComponent()
过度转义。
希望这对某人有帮助。