简体   繁体   English

使用POST的基于Amazon AWS S3浏览器的上传 -

[英]Amazon AWS S3 browser-based upload using POST -

I am building a web application that includes a file upload feature. 我正在构建一个包含文件上传功能的Web应用程序。 My goal is to initiate upload from users directly to an S3 bucket. 我的目标是从用户直接上传到S3存储桶。 The strategy is to pre-sign a POST request that will get submitted as a form. 策略是预先签署将作为表单提交的POST请求。

The roadblock is a SignatureDoesNotMatch error - as far as I can tell I've conformed to the documentation, and have explored a lot of options, but still unable to resolve. 包版是一个SignatureDoesNotMatch错误 - 据我所知,我已经符合文档,并且已经探索了很多选项,但仍然无法解决。 I am able to generate presigned download links. 我能够生成预先签名的下载链接。

Referencing: 引用:

AWS POST documentation AWS POST文档

Example

boto3 generate_presigned_post reference boto3 generate_presigned_post参考

Generate signed request: 生成签名请求:

def s3_upload_creds(name, user):
    s3 = boto3.client('s3')
    key = '${filename}'
    region = 'us-east-1'
    date_short = datetime.datetime.utcnow().strftime('%Y%m%d')
    date_long = datetime.datetime.utcnow().strftime('%Y%m%dT000000Z')
    fields = { 
        'acl': 'private',
        'date': date_short,
        'region': region,
        'x-amz-algorithm': 'AWS4-HMAC-SHA256',
        'x-amz-date': date_long
    }

    return s3.generate_presigned_post(
        Bucket = 'leasy',
        Fields = fields,
        Key = key,
        Conditions = [
            {'acl': 'private'},
            {'x-amz-algorithm': 'AWS4-HMAC-SHA256'},
            {'x-amz-credential': '/'.join(['AKI--snip--', date_short, region, 's3', 'aws4_request'])},
            {'x-amz-date': date_long}
        ]
    )

Upload form (populated with fields above): 上传表单(填充上面的fields ):

<html>
  <head>

    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />

  </head>
  <body>
  {{ creds }}
  <form action="{{ creds.url }}" method="post" enctype="multipart/form-data">
    Key to upload: 
    <input type="input"  name="key" value="${filename}" /><br />
    <input type="input"  name="acl" value="{{ creds.fields.acl }}" />
    <input type="hidden" name="Policy" value="{{ creds.fields.policy }}" />
    <input type="text"   name="X-Amz-Algorithm" value="{{ creds.fields['x-amz-algorithm'] }}" />
    <input type="input"   name="X-Amz-Credential" value="{{ creds.fields.AWSAccessKeyId }}/{{ creds.fields.date }}/us-east-1/s3/aws4_request" />
    <input type="input"   name="X-Amz-Date" value="{{ creds.fields['x-amz-date'] }}" />
    <input type="input" name="X-Amz-Signature" value="{{ creds.fields.signature }}" />
    File: 
    <input type="file"   name="file" /> <br />
    <!-- The elements after this will be ignored -->
    <input type="submit" name="submit" value="Upload to Amazon S3" />
  </form>

</html>

Relevant portion of response: 响应的相关部分:

<Error>
<Code>SignatureDoesNotMatch</Code>
<Message>
The request signature we calculated does not match the signature you provided. Check your key and signing method.
</Message>
<AWSAccessKeyId>AKI--snip--</AWSAccessKeyId>
<StringToSign>
eyJjb25kaXRpb25zIjogW3siYWNsIjogInByaXZhdGUifSwgeyJ4LWFtei1hbGdvcml0aG0iOiAiQVdTNC1ITUFDLVNIQTI1NiJ9LCB7IngtYW16LWNyZWRlbnRpYWwiOiAiQUtJQUlDVjRNVlBUUlFHU1lLV1EvMjAxNTEyMTgvdXMtZWFzdC0xL3MzL2F3czRfcmVxdWVzdCJ9LCB7IngtYW16LWRhdGUiOiAiMjAxNTEyMThUMDAwMDAwWiJ9LCB7ImJ1Y2tldCI6ICJsZWFzeSJ9LCBbInN0YXJ0cy13aXRoIiwgIiRrZXkiLCAiIl1dLCAiZXhwaXJhdGlvbiI6ICIyMDE1LTEyLTE4VDA1OjEwOjU2WiJ9
</StringToSign>
<SignatureProvided>wDOjsBRc0iIW7JNtz/4GHgfvKaU=</SignatureProvided>

Base64 decode of StringToSign in above error: StringToSign Base64解码错误如下:

{u'conditions': [{u'acl': u'private'},
                 {u'x-amz-algorithm': u'AWS4-HMAC-SHA256'},
                 {u'x-amz-credential': u'AKI--snip--/20151218/us-east-1/s3/aws4_request'},
                 {u'x-amz-date': u'20151218T000000Z'},
                 {u'bucket': u'leasy'},
                 [u'starts-with', u'$key', u'']],
 u'expiration': u'2015-12-18T04:59:32Z'}

Found a solution: had to explicitly configure the s3 client to use Amazon's new signature v4. 找到解决方案:必须明确配置s3客户端以使用亚马逊的新签名v4。 The error occurs since it defaults to an older version, causing the mismatch. 发生错误,因为它默认为旧版本,导致不匹配。 Bit of a facepalm - at the time this wasn't written in boto3 docs, although folks at Amazon say it should be soon. 有点像facepalm - 当时这不是用boto3文档编写的,虽然亚马逊的人说它应该很快。

The method is simplified since it now returns exactly the fields required: 该方法简化,因为它现在返回所需的字段:

def s3_upload_creds(name):
    BUCKET = 'mybucket'
    REGION = 'us-west-1'
    s3 = boto3.client('s3', region_name=REGION, config=Config(signature_version='s3v4'))
    key = '${filename}'
    return s3.generate_presigned_post(
        Bucket = BUCKET,
        Key = key
    )

Which means the form can be easily generated: 这意味着可以轻松生成表单:

<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
  </head>
  <body>
    {{ creds }}
      <form action="https://mybucket.s3.amazonaws.com" method="post" enctype="multipart/form-data">
        {% for key, value in creds.fields.items() %}
          <input type="hidden" name="{{ key }}" value="{{ value }}" />
        {% endfor %}
      File:
      <input type="file"   name="file" /> <br />
    <input type="submit" name="submit" value="Upload to Amazon S3" />
  </form>
</html>

Cheers 干杯

Been a few years since the last response, but I've been stuck on this for the last day or two so I'll share my experience for anyone it may help. 自上次回复以来已经过了几年,但是我在最后一两天一直坚持这一点,所以我会分享我的经验给任何可能有帮助的人。

I had been getting the error: "403: The AWS Access Key Id you provided does not exist in our records" when trying to upload to an s3 bucket via my presigned url. 当我尝试通过我的预签名网址上传到s3存储桶时,我收到了错误: “403:您提供的AWS Access密钥ID在我们的记录中不存在”

I was able to successfully generate a presigned url similarly to above, using the server-side code: 我能够使用服务器端代码成功生成与上面类似的预签名URL:

signed_url_dict = self.s3_client.generate_presigned_post(
                self.bucket_name,
                object_name,
                ExpiresIn=300

This returned a dictionary with the structure: 这返回了一个结构字典:

{
    url: "https://___",
    fields: {
        key: "___",
        AWSAccesKeyId: "___",
        x-amz-security-token: "___",
        policy: "___",
        signature: "___"
    }
}

This lead to the part where things were a little different now in 2019 with the browser-side javascript, where the required form inputs seem to have changed. 这导致了2019年与浏览器端javascript有些不同的部分,其中所需的表单输入似乎已经改变。 Instead of setting up the form as OP did, I had to create my form as seen below: 而不是像OP那样设置表单,我必须创建我的表单,如下所示:

<form action="https://pipeline-poc-ed.s3.amazonaws.com/" method="post" enctype="multipart/form-data" name="upload_form">
            <!-- Copy ALL of the 'fields' key:values returned by S3Client.generate_presigned_post() -->
            <input type="hidden" name="key" value="___" />
            <input type="hidden" name="AWSAccessKeyId" value="___" />
            <input type="hidden" name="policy" value="___"/>
            <input type="hidden" name="signature" value="___" />
            <input type="hidden" name="x-amz-security-token" value="___" />
        File:
            <input type="file"   name="file" /> <br />
            <input type="submit" name="submit" value="Upload to Amazon S3" />
        </form>

My error was that I followed an example in the boto3 1.9.138 docs and left out "x-amz-security-token" on the form, which turned out to be quite necessary. 我的错误是我在boto3 1.9.138文档中跟踪了一个示例,并在表单上省略了“x-amz-security-token”,结果证明是非常必要的。 A thoughtless oversight on may part, but hopefully this will help someone else. 一个轻率的疏忽可能部分,但希望这将有助于其他人。

EDIT: My results above were based on a N. Virginia Lambda Function. 编辑:我上面的结果基于N. Virginia Lambda函数。 When I ran generate_presigned_post(...) in Ohio (the region containing my bucket), I got results similar to OP: 当我在俄亥俄州(包含我的桶的区域generate_presigned_post(...)中运行generate_presigned_post(...)时,我得到的结果类似于OP:

{
  "url": "https://__",
  "fields": {
    "key": "___",
    "x-amz-algorithm": "___",
    "x-amz-credential": "___",
    "x-amz-date": "___",
    "x-amz-security-token": "___",
    "policy": "___",
    "x-amz-signature": "___"
  }
}

Perhaps the results of the function are region specific? 也许功能的结果是区域特定的?

In my case, I was generating a form with Base64-encoded. 就我而言,我正在使用Base64编码生成一个表单。

The problem was due to Firefox inherently encoding the Policy and Security token values into Base64-encoded on top of it. 问题是由于Firefox本身将策略和安全性令牌值编码为Base64编码。

Thus there was double encoding and therefore the signature did not match as required. 因此存在双重编码,因此签名不符合要求。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM