【问题标题】:AWS Presigned URL works with Python's Requests library but fails with cURLAWS Presigned URL 可与 Python 的 Requests 库一起使用,但使用 cURL 失败
【发布时间】:2019-10-04 10:42:26
【问题描述】:

最近我开始使用 AWS 预签名 URL 将文件上传到 S3。使用 Python 的 Requests 库时,生成的预签名 URL 可以正常工作,如下所示:

生成预签名网址:

def create_presigned_post(bucket_name, object_name,
                          fields=None, conditions=None, expiration=3600):
    """Generate a presigned URL S3 POST request to upload a file

    :param bucket_name: string
    :param object_name: string
    :param fields: Dictionary of prefilled form fields
    :param conditions: List of conditions to include in the policy
    :param expiration: Time in seconds for the presigned URL to remain valid
    :return: Dictionary with the following keys:
        url: URL to post to
        fields: Dictionary of form fields and values to submit with the POST
    :return: None if error.
    """

    # Generate a presigned S3 POST URL
    s3_client = boto3.client('s3')
    try:
        response = s3_client.generate_presigned_post(bucket_name,
                                                     object_name,
                                                     Fields=fields,
                                                     Conditions=conditions,
                                                     ExpiresIn=expiration)
    except ClientError as e:
        logging.error(e)
        return None

    # The response contains the presigned URL and required fields
    return response

运行请求以获取预签名的 url


# Getting a presigned_url to upload the file into S3 Bucket.
        headers = {'Content-type': 'application/json', 'request': 'upload_url', 'target': FILENAME, 'x-api-key': API_KEY}

        r_upload = requests.post(url = API_ENDPOINT, headers = headers)

        url = json.loads(json.loads(r_upload.text)['body'])['url']
        fields_ = json.loads(json.loads(r_upload.text)['body'])['fields']
        fields = {
                "x-amz-algorithm": fields_["x-amz-algorithm"],
                "key": fields_["key"],
                "policy": fields_["policy"],
                "x-amz-signature": fields_["x-amz-signature"],
                "x-amz-date": fields_["x-amz-date"],
                "x-amz-credential": fields_["x-amz-credential"],
                "x-amz-security-token":  fields_["x-amz-security-token"]
        }

        fileobj = open(FILENAME, 'rb')
        http_response = requests.post(url, data=fields,files={'file': (FILENAME, fileobj)})

有效响应

 "{\"url\": \"https://****.s3.amazonaws.com/\", 
   \"fields\": 
        {\"key\": \"******\", \"x-amz-algorithm\": \"*******\", \"x-amz-credential\": \"*******\", \"x-amz-date\": \"*********\", \"x-amz-security-token\": \"********", \"policy\": \"**********\", \"x-amz-signature\": \"*******\"}}

如您所见,在使用生成的预签名 URL 上传文件时,我没有提供 AWSAccessKey 或任何凭据,这是非常合乎逻辑的,因为创建预签名 URL 是为了提供给外部用户使用此类 URL 时无需提供凭据。

但是,当尝试使用 cURL 运行 Python 的 Requests 库发出的相同调用时,请求失败并出现错误:

< HTTP/1.1 403 Forbidden
<Error><Code>AccessDenied</Code><Message>Access Denied</Message><Error>

为了得到requests.post 发出的确切请求调用,我正在运行:

req = http_response.request
command = "curl -X {method} -H {headers} -d '{data}' '{uri}'"
method = "PUT"
uri = req.url
data = req.body
headers = ['"{0}: {1}"'.format(k, v) for k, v in req.headers.items()]
headers = " -H ".join(headers)
print(command.format(method=method, headers=headers, data=data, uri=uri))

返回:

curl -v -X PUT -H "Connection: keep-alive" --upload-file xxxx.zip -H "Accept-Encoding: gzip, deflate" -H "Accept: */*" -H "User-Agent: python-requests/2.18.4" -H "Content-Length: xxxx" -H "Content-Type: multipart/form-data; boundary=8a9864bdxxxxx00100ba04cc055a" -d '--8a9864bd377041xxxxx04cc055a
Content-Disposition: form-data; name="x-amz-algorithm"
AWS4-HMAC-SHA256

--8a9864bd377041e0b00100ba04cc055a
Content-Disposition: form-data; name="key"
xxxxx.zip

--8a9864bd377041e0b00100ba04cc055a
Content-Disposition: form-data; name="x-amz-signature"
*****

--8a9864bd377041e0b00100ba04cc055a
Content-Disposition: form-data; name="x-amz-security-token"
*****

--8a9864bd377041e0b00100ba04cc055a
Content-Disposition: form-data; name="x-amz-date"
*****

--8a9864bd377041e0b00100ba04cc055a
Content-Disposition: form-data; name="policy"
*****

--8a9864bd377041e0b00100ba04cc055a
Content-Disposition: form-data; name="x-amz-credential"
xxxxx/xxxxx/xxxx/s3/aws4_request

' 'https://xxxxx.s3.amazonaws.com/'

然后重新制定它:

$ curl -v -T file "https://****.s3.amazonaws.com/?key=************&x-amz-algorithm=***************&x-amz-credential=*************&x-amz-security-token=************&policy=**********&x-amz-signature=****************

经过研究,我没有发现与此问题相似的内容,但是: https://aws.amazon.com/es/premiumsupport/knowledge-center/s3-access-denied-error/

这对我来说似乎仍然不合逻辑,因为在使用预签名 URL 时我不应该输入任何凭据。

我不知道我是否遗漏了 Python 的 Requests 库发出的完整请求。

任何想法,请!

亲切的问候,

Rshad

【问题讨论】:

    标签: amazon-web-services curl python-requests pre-signed-url


    【解决方案1】:

    这个简单的 curl 命令应该可以工作:

    使用通常的预签名网址,如下所示:

    curl -v \
    -F key=<filename> \
    -F x-amz-algorithm=*** \
    -F x-amz-credential=*** \
    -F x-amz-date=*** \
    -F x-amz-security-token=*** \
    -F policy=*** \
    -F x-amz-signature=*** \
    -F file=@<filename> \
    'https://<bucket>.s3.amazonaws.com/'
    

    -F 字段允许您指定应上传到 S3 的附加 POST 数据(即从带有预签名 URL 的字段数据返回。

    亲切的问候,

    【讨论】:

    • 嗨阿萨曼!请再次检查描述,我对其进行了更详细的调整
    • 嗯,可以设置 Content-Type 标头并将 URL 括在单引号中。当您生成预签名 URL 时,您期望什么内容类型?
    • @Rshad 在这方面有什么进展吗?
    • 嗨@Ashman!抱歉耽搁了。我已经尝试在标题中设置内容类型并将 URL 括在单引号中,但没有任何改变。结果,我正在等待“application/json”内容类型。
    • @Rshad 你能把你用来生成URL的代码贴出来吗,我会在本地试试。
    猜你喜欢
    • 1970-01-01
    • 2020-02-26
    • 2020-01-29
    • 2013-03-26
    • 2019-01-04
    • 2014-08-25
    • 2015-03-11
    • 1970-01-01
    • 2021-08-09
    相关资源
    最近更新 更多