无论我做什么,都无法使用预签名 URL 将文件上传到 S3。 AWS 命令行有效。CURL 和其他任何东西 = 403



我在~/.aws/credentials中的AWS凭据是正确的并且正在工作。证明吗?

$ aws s3api put-object --bucket <my bucket name> --key videos/uploads/yoda.jpeg --body /Users/r<my_name>/Desktop/Archive/yoda.jpeg

回到:

{
"ETag": ""66bee0b7caf3d127900e0a70f2da4b5f""
}

从命令行上传。当我在AWS的管理控制台中看到我的S3桶时,我可以看到我的文件。

现在-我从S3中删除了成功上传的文件,并试图再次上传,这次是通过预先指定的URL

$ aws s3 presign s3://<my-bucket>/videos/uploads/yoda.jpeg

得到:

https://<my-bucket>.s3.amazonaws.com/videos/uploads/yoda.jpeg?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Expires=3600&X-Amz-Credential=<MY-AWS-KEY-ID>%2F20210207%2Fus-east-2%2Fs3%2Faws4_request&X-Amz-SignedHeaders=host&X-Amz-Date=20210207T222859Z&X-Amz-Signature=3a3624b9e264c119ebdf93c989efb73337f7ab8793e89554c7b000e1fc93c85c

从这一刻起,任何PUT尝试使用CURL,POSTMAN或任何其他工具,使用此URL上传文件失败总是以403结束(是的,它没有过期,它立即失败),The request signature we calculated does not match the signature you provided是AWS提供的借口。

S3 bucket有一个策略,允许凭据在/.aws/credentialsPut*的用户在该bucket上使用。

怎么回事?为什么不预先签名的URL工作?

<旋度尝试/strong>

$ curl --location --request PUT 'https://<my-bucket-name>.s3.amazonaws.com/videos/uploads/yoda.jpeg?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Expires=3600&X-Amz-Credential=<MY-AWS-KEY-ID>%2F20210207%2Fus-east-2%2Fs3%2Faws4_request&X-Amz-SignedHeaders=host&X-Amz-Date=20210207T224403Z&X-Amz-Signature=8a8625591e6c4e0871f97bf5e15c2f93b3e373cfc1c2daddb2cf34edb10a5670%0A' 
--header 'Content-Type: image/jpeg' 
--data-binary '@/Users/<MY-NAME>/Desktop/Archive/yoda.jpeg'

得到:

<?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>
<AWSAccessKeyId><---MY--ACCESS--KEY--ID--->/AWSAccessKeyId>
<StringToSign>AWS4-HMAC-SHA256
20210207T224403Z
20210207/us-east-2/s3/aws4_request
da93cc1a0ec196fe0726ec6d5cace8c1b2b4865b20663bf0240454e276dbef6f</StringToSign>
<SignatureProvided>8a8625591e6c4e0871f97bf5e15c2f93b3e373cfc1c2daddb2cf34edb10a5670
</SignatureProvided>
<StringToSignBytes>41 57 53 34 2d 48 4d 41 43 2d 53 48 41 32 35 36 0a 32 30 32 31 30 32 30 37 54 32 32 34 34 30 33 5a 0a 32 30 32 31 30 32 30 37 2f 75 73 2d 65 61 73 74 2d 32 2f 73 33 2f 61 77 73 34 5f 72 65 71 75 65 73 74 0a 64 61 39 33 63 63 31 61 30 65 63 31 39 36 66 65 30 37 32 36 65 63 36 64 35 63 61 63 65 38 63 31 62 32 62 34 38 36 35 62 32 30 36 36 33 62 66 30 32 34 30 34 35 34 65 32 37 36 64 62 65 66 36 66</StringToSignBytes>
<CanonicalRequest>PUT
/videos/uploads/yoda.jpeg
X-Amz-Algorithm=AWS4-HMAC-SHA256&amp;X-Amz-Credential=<---MY--ACCESS--KEY--ID--->%2F20210207%2Fus-east-2%2Fs3%2Faws4_request&amp;X-Amz-Date=20210207T224403Z&amp;X-Amz-Expires=3600&amp;X-Amz-SignedHeaders=host
host:<my-bucket-name>.s3.amazonaws.com
host
UNSIGNED-PAYLOAD</CanonicalRequest>
<CanonicalRequestBytes>50 55 54 0a 2f 76 69 64 65 6f 73 2f 75 70 6c 6f 61 64 73 2f 79 6f 64 61 2e 6a 70 65 67 0a 58 2d 41 6d 7a 2d 41 6c 67 6f 72 69 74 68 6d 3d 41 57 53 34 2d 48 4d 41 43 2d 53 48 41 32 35 36 26 58 2d 41 6d 7a 2d 43 72 65 64 65 6e 74 69 61 6c 3d 41 4b 49 41 51 33 44 34 36 52 4e 50 48 51 4e 4b 47 42 46 4b 25 32 46 32 30 32 31 30 32 30 37 25 32 46 75 73 2d 65 61 73 74 2d 32 25 32 46 73 33 25 32 46 61 77 73 34 5f 72 65 71 75 65 73 74 26 58 2d 41 6d 7a 2d 44 61 74 65 3d 32 30 32 31 30 32 30 37 54 32 32 34 34 30 33 5a 26 58 2d 41 6d 7a 2d 45 78 70 69 72 65 73 3d 33 36 30 30 26 58 2d 41 6d 7a 2d 53 69 67 6e 65 64 48 65 61 64 65 72 73 3d 68 6f 73 74 0a 68 6f 73 74 3a 6c 73 74 76 32 2d 70 75 62 6c 69 63 2e 73 33 2e 61 6d 61 7a 6f 6e 61 77 73 2e 63 6f 6d 0a 0a 68 6f 73 74 0a 55 4e 53 49 47 4e 45 44 2d 50 41 59 4c 4f 41 44</CanonicalRequestBytes>
<RequestId>CBJT0Y4SX9A7RB26</RequestId>
<HostId>h+5b/u8cdi34yuSDBX0Z/mZGQMtRZIMS4rvIwiKzOZSOZhRoQfak8cOdVBq2BgtU1qbqlHrO2TY=</HostId>
</Error>

尝试从PYTHON生成指定URL。还是不行。url有问题——aws拒绝使用相同的403

def get_upload_pre_signed_url(bucket_name, object_name, expiration=3600):
s3_client = boto3.client('s3')
try:
response = s3_client.generate_presigned_url('put_object',
Params={'Bucket': bucket_name,
'Key': object_name},
ExpiresIn=expiration)
except ClientError as e:
return None

# The response contains the presigned URL
return response

生成的URL:

https://<my-bucket>.s3.amazonaws.com//videos/uploads/yoda.jpg?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=<my-AWS-KEY-ID>%2F20210207%2Fus-east-2%2Fs3%2Faws4_request&X-Amz-Date=20210207T231306Z&X-Amz-Expires=3600&X-Amz-SignedHeaders=host&X-Amz-Signature=968a3e2cab9b7e907df69e24aae24d79ea40f52a52d407591d7cbd69c86fe67b

卷曲得到相同的403。没有改变。

aws s3 presign命令创建可用于下载文件的url。它不创建可用于上传的url。引用文档-

为Amazon S3对象生成预签名URL。这允许任何接收到预签名URL的人使用HTTP GET请求检索S3对象。所有预先指定的URL现在都使用sigv4,因此需要显式配置该区域。

要创建上传url,您需要跳出命令行,进入您选择的语言,并使用完整的AWS SDK。

Python示例:

s3.generate_presigned_post(
Bucket=BUCKET_NAME,
Key=FILE_KEY,
ExpiresIn=(5*60)
)

请注意,它使用generate_presigned_post函数来完成此操作。

解决方案:原来我使用的Boto3不是最新的,我用错了。修复这些之后,我的代码是:

# THE CREDENTIALS ARE PART OF MY TESTING CODE. NO WORRIES THEY'RE IN AN ENV VARIABLE NOW
def get_upload_pre_signed_url(bucket_name, key, expiration=3600):
s3 = boto3.client('s3',
aws_access_key_id="<my access_key_id",
aws_secret_access_key="<my_secreet_access_key>",
config=Config(region_name='us-east-2', s3.{"use_accelerate_endpoint": True}))
try:
url = s3.generate_presigned_url('put_object', Params={'Bucket': bucket_name, 'Key': key},
ExpiresIn=expiration,
HttpMethod='PUT')
except ClientError as e:
return None
return url

presign cli命令仅用于GET请求。如果你需要其他东西,你必须直接使用AWS API——如下所示:你可以使用一个简短的python脚本。我们在我们的一个应用中使用了lambda,你可以调用它,你会得到正确的url。另外,预先指定的URL使用调用API的角色,因此它具有相同的权限。包括这样一个事实,如果您正在使用和STS假定的角色,并且授权到期时间早于预先指定的url的到期时间,url仍然会失败。但是如果您使用常规角色(比如您的aws cli配置文件),应该没问题。

https://awscli.amazonaws.com/v2/documentation/api/latest/reference/s3/presign.html

为Amazon S3对象生成预签名URL。这允许任何接收到预签名URL的人使用HTTP GET请求检索S3对象。所有预先指定的URL现在都使用sigv4,因此需要显式配置该区域。

可能发送一个PUT请求到aws s3指定url?

AWS CLI不支持预先指定的PUT URL。不过,您可以使用Python Boto3轻松生成一个。文档在这里。如果你想要一个预先指定的PUT,你只需要让ClientMethod参数为put_object。

s3 presign仅用于生成下载url

为Amazon S3对象生成预签名URL。这允许任何人谁接收到预先签名的URL,用HTTP检索S3对象GET请求。所有预先指定的URL现在都使用sigv4,所以该区域需要显式配置

我们仍然需要指定要上传的Bucket和Key。

NodeJs:

const bucketParms = {
Bucket: "sample-temp-bucket",
Key: "HeadShot.jpg",
ContentType:'image/jpeg'
};
s3.getSignedUrl("putObject", bucketParms, (error, url) => {
if (error) console.log("error", error);
if (url) console.log("url", url);
});

Python:

response = s3_client.generate_presigned_url('put_object',
Params={'Bucket': bucket_name,
'Key': object_name,
'ContentType':'image/jpeg'},
ExpiresIn=expiration)

我们可以做curl或postman

curl --location --request PUT 'https://test-events.s3.amazonaws.com/98..?X....' 
--header 'Content-Type: image/jpeg' 
--data-binary '/Users/user/image/path'

对于使用SDK生成URL,您的编辑是正确的。

也就是说,要使用URL, curl使用的HTTP头必须完全正确。值得注意的是,签名不需要向服务器发送Content-Type报头。由于--data-binary强制一个,我知道get旋度做正确事情的最简单方法是使用--upload-file标志:

$ curl $URL --upload-file yoda.jpg

它也不适合我,它返回403禁止。有帮助的是:

  • 设置签名为v4new AWS.S3({ signatureVersion: "v4" })
  • 如果您使用元数据,请对内容进行编码encodeURI("meta text with symbols#@");

下面是更多的ts/js代码:

const s3 = new AWS.S3({ signatureVersion: "v4" });
const s3Params = {
Bucket: <BUCKET_NAME>,
Key: <KEY>,
Expires: 300,
ContentType: <CONTENT_TYPE>,
Metadata: {
title: encodeURI('SomeTitle with utf-8 characters'),
},
};
const signedPutUrl = await s3.getSignedUrlPromise(
"putObject",
s3Params
);
console.log('the signed url',signedPutUrl);
return {'url':signedPutUrl};

最新更新