使用ReactJs/NodeJS预设url上传图片到s3会抛出403错误SignatureDoesNotMatch.&l



我已经在App1中使用预先指定的url成功地将图像上传到S3.
我试图在App 2做同样的事情,但我一直得到相同的错误:

403 SignatureDoesNotMatch


前端:

import React from "react";
import { uploadAdImageToS3BucketAndReturnImageUrl } from "../../../api/calls/data/ads/ads-configuration";
export default function UploadImageTutorial1() {
const onSelectFile = async (event) => {
const file = event.target.files[0];
const { type, name } = file;
// This throws an error
await uploadAdImageToS3BucketAndReturnImageUrl(file, type);
};
return (
<div>
<input type="file" accept="image/*" onChange={onSelectFile} />;
</div>
);
}

api/calls.js

export const uploadAdImageToS3BucketAndReturnImageUrl = async (
imageFile,
typeFile
) => {
try {
const [axios, cancel] = useAxios();
const { data: uploadConfig } = await axios.get(
"/ads/get_image_presigned_url"
);
const saved_image_url = await uploadImageFileToCloud(
uploadConfig,
imageFile,
typeFile
);
return saved_image_url;
} catch (e) {}
};
const uploadImageFileToCloud = async (uploadConfig, imageFile, typeFile) => {
const [axios, cancel] = useAxios();
try {
// This throws 400 error
await axios.put(uploadConfig.url, imageFile, {
headers: {
"Content-Type": typeFile,
"x-amz-acl": "public-read",
},
transformRequest: (data, headers) => {
delete headers.common["Authorization"];
return data;
},
});
} catch (error) {
console.log("🚀 error", error);
}
};

后台:

s3-config.js

// Load the SDK for JavaScript
const { config } = require("dotenv");
config();
var AWS = require("aws-sdk");
const REGION = "xxxxxxxxx"; //e.g. "us-east-1"
AWS.config.update({ region: REGION });
const { AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_BUCKET_NAME } =
process.env;

const s3 = new AWS.S3({
accessKeyId: AWS_ACCESS_KEY_ID,
secretAccessKey: AWS_SECRET_ACCESS_KEY,
signatureVersion: "v4",
});
var getImageSignedUrl = async function (key) {
return new Promise((resolve, reject) => {
s3.getSignedUrl(
"putObject",
{
Bucket: AWS_BUCKET_NAME,
Key: key,
ContentType: "image/*",
ACL: "public-read",
Expires: 300,
},
(err, url) => {
if (err) {
reject(err);
} else {
resolve(url);
}
}
);
});
};
exports.getImageSignedUrl = getImageSignedUrl;

路线/upload.js

// @route  POST api/ads/get_image_presigned_url
// @desc   Get image presigned url
// @access Private
router.get("/get_image_presigned_url", (req, res) => {
const key = `images/ad_${Date.now()}.jpeg`;
s3_config
.getImageSignedUrl(key)
.then((url) => {
res.status(200).send({ key, url });
})
.catch((error) => {
res.status(500).send({
message: "There was an error generating pre-signed url.",
});
});
});

当我尝试使用上面的代码上传图像时,在前端,这个错误被记录:

https://bucket-name.s3.region.amazonaws.com/images/file_name.jpeg?Content-Type=image%2F%2A&X-Amz-Algorithm=XXX-XXX-SHA256&X-Amz-Credential=XXXXXXX%2F20220526%2Feu-west-3%2Fs3%2Faws4_request&X-Amz-Date=20220526T121600Z&X-Amz-Expires=300&X-Amz-Signature=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&X-Amz-SignedHeaders=host%3Bx-amz-acl&x-amz-acl=public-read400(错误请求)

当我点击它时,它会在一个新页面中打开:

<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>XXXXXXXXXXXXXXXXXXXXXx</AWSAccessKeyId>
<StringToSign>
AWS4-XXXX-XXXX 20220526T121600Z 20220526/region/s3/aws4_request
d058c00da90b745bd2xxxxxxxxxxxxxxxxd68b3c537ff
</StringToSign>
<SignatureProvided>
9b372d73xxxxxxxxxxxxxxxxxxxxxx069c90ab7e24193990f1bcd39a0
</SignatureProvided>
<StringToSignBytes>
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 36
54 31 32 31 36 30 30 5a 0a 32 30 32 32 30 35 32 36 2f 65 75 2d 77 65 73 74
2d 33 2f 73 33 2f 61 77 73 34 5f 72 65 71 75 65 73 74 0a 64 30 35 38 63 30
30 64 61 39 30 62 37 34 35 62 64 32 66 66 37 62 35 33 31 39 62 38 61 37 32
33 34 36 37 35 33 65 64 63 31 38 62 63 62 36 38 37 31 65 62 63 39 64 36 38
62 33 63 35 33 37 66 66
</StringToSignBytes>
<CanonicalRequest>
GET /images/image_name.jpeg
Content-Type=image%2F%2A&X-Amz-Algorithm=AWS4-XXXX-XXXXX&X-Amz-Credential=XXXXXXXXXXXX%2F20220526%2Feu-west-3%2Fs3%2Faws4_request&X-Amz-Date=20220526T121600Z&X-Amz-Expires=300&X-Amz-SignedHeaders=host%3Bx-amz-acl&x-amz-acl=public-read
host:bucket-name.s3.region.amazonaws.com x-amz-acl:public-read
host;x-amz-acl UNSIGNED-PAYLOAD
</CanonicalRequest>
<CanonicalRequestBytes>
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 30
32 31 38 2e 6a 70 65 67 0a 43 6f 6e 74 65 6e 74 2d 54 79 70 65 3d 69 6d 61
67 65 25 32 46 25 32 41 26 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 52 5a 41 52 52 50 50 49 42 4d 56 45
57 4b 55 57 25 32 46 32 30 32 32 30 35 32 36 25 32 46 65 75 2d 77 65 73 74
2d 33 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 32 30 35 32 36 54 31 32 31 36 30 30 5a
26 58 2d 41 6d 7a 2d 45 78 70 69 72 65 73 3d 33 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 25 33 42 78 2d 61 6d
7a 2d 61 63 6c 26 78 2d 61 6d 7a 2d 61 63 6c 3d 70 75 62 6c 69 63 2d 72 65
61 64 0a 68 6f 73 74 3a 6c 6f 64 65 65 70 2d 73 74 6f 72 61 67 65 2d 33 2e
73 33 2e 65 75 2d 77 65 73 74 2d 33 2e 61 6d 61 7a 6f 6e 61 77 73 2e 63 6f
6d 0a 78 2d 61 6d 7a 2d 61 63 6c 3a 70 75 62 6c 69 63 2d 72 65 61 64 0a 0a
68 6f 73 74 3b 78 2d 61 6d 7a 2d 61 63 6c 0a 55 4e 53 49 47 4e 45 44 2d 50
41 59 4c 4f 41 44
</CanonicalRequestBytes>
<RequestId>XXXXXXXXXXX</RequestId>
<HostId>
q1cQMi/q0jrkk3SeLd3F8/v/mx62XXXXXXXXXXXXXQbVhCdZCDBNfnluTruGLnkhM=
</HostId>
</Error>;

奇怪的是SignatureDoesNotMatch在他们的文档中是403错误。

我知道其他人也面临着同样的问题,但它似乎是由不同的原因引发的,我仍然没有发现是什么原因造成的。
在这里,在一个回答中,提到了这一点:

向S3发出签名请求时,AWS检查以确保签名与浏览器的HTTP头信息完全匹配发送。

这可能是导致问题的原因。但是,我不知道具体是什么。

在您的s3-config.js文件中,您有以下内容:

s3.getSignedUrl(
"putObject",
{
Bucket: AWS_BUCKET_NAME,
Key: key,
ContentType: "image/*",
ACL: "public-read",
Expires: 300,
},
(err, url) => {
if (err) {
reject(err);
} else {
resolve(url);
}
}
);

但是,为PUT method预先设计的url不支持内容类型中的通配符。一种解决方案是将文件类型传递给函数,并动态设置它。像这样:

var getImageSignedUrl = async function (key, fileType) {
return new Promise((resolve, reject) => {
s3.getSignedUrl(
"putObject",
{
Bucket: AWS_BUCKET_NAME,
Key: key,
ContentType: fileType,
ACL: "public-read",
Expires: 300,
},
(err, url) => {
if (err) {
reject(err);
} else {
resolve(url);
}
}
);
});
};

这需要你添加一个fileType parameter到你的GET方法,当你得到你预先指定的url。如果有帮助,请告诉我:)

注意:你还必须在你的头文件中提供相同的内容类型,当你调用上传你的对象到预先指定的url时,但在你的代码中,它看起来已经在那里了。

最新更新