使用签名URL上传到S3时获取403(禁止)
我试图生成预签名的URL,然后通过浏览器将文件上传到S3。我的服务器端代码如下所示,并生成以下URL:使用签名URL上传到S3时获取403(禁止)
let s3 = new aws.S3({
// for dev purposes
accessKeyId: 'MY-ACCESS-KEY-ID',
secretAccessKey: 'MY-SECRET-ACCESS-KEY'
});
let params = {
Bucket: 'reqlist-user-storage',
Key: req.body.fileName,
Expires: 60,
ContentType: req.body.fileType,
ACL: 'public-read'
};
s3.getSignedUrl('putObject', params, (err, url) => {
if (err) return console.log(err);
res.json({ url: url });
});
这部分似乎正常工作。如果我登录它并将它传递到前端,我可以看到该URL。然后在前端,我试图用axios和签名的URL上传文件:
.then(res => {
var options = { headers: { 'Content-Type': fileType } };
return axios.put(res.data.url, fileFromFileInput, options);
}).then(res => {
console.log(res);
}).catch(err => {
console.log(err);
});
}
这样,我得到了403 Forbidden错误。如果我按照链接,有一些XML更多信息:
<Error>
<Code>SignatureDoesNotMatch</Code>
<Message>
The request signature we calculated does not match the signature you provided. Check your key and signing method.
</Message>
...etc
您的请求需要与签名完全匹配。一个显而易见的问题是,即使您将它包含在签名中,实际上您并没有在请求中包含预设的ACL。更改为:
var options = { headers: { 'Content-Type': fileType, 'x-amz-acl': 'public-read' } };
1)您可能需要使用S3V4签名取决于如何将数据传输到AWS(对块流)。创建客户端如下:
var s3 = new AWS.S3({
signatureVersion: 'v4'
});
2)不要添加新标头或修改现有标头。该请求必须完全符合签名。
3)确保生成的url与发送给AWS的内容匹配。
4)做一个测试请求,在签名前删除这两行(并从你的PUT中删除头文件)。这将有助于缩小您的问题:
ContentType: req.body.fileType,
ACL: 'public-read'
谢谢,我跑过这个列表,我相信也许有一个标题不匹配。如何比较签名url中的标题和我发送的标题? – Alan
你在调用getSignedUrl时在你的params中指定HTTP头。还要注意Michael sqlbot的回复x-amz-acl头。迈克尔的参数是正确的。确保你在你的PUT中包含相同的头文件。 –
一个额外的项目。在签名和调用放入时,使用文件大小添加Content-Length。我没有看到指定的区域。创建客户端时添加此项。 –
有同样的问题,这里是你需要如何去解决它,
- 提取标识的URL的文件名部分。 执行打印,您正在使用查询字符串参数正确提取您的文件名部分。这很关键。
- 使用查询字符串参数编码到URI的文件名编码。
- 从编码文件名连同其他路径或从您的节点服务中返回您的lambda网址。
现在从axios与该网址发布,它将工作。
EDIT1: 如果您传递错误的内容类型,您的签名也将无效。
请确保您创建预签名url的内容类型与您正在使用它的内容类型相同。
希望它有帮助。
谢谢,这不是,但我觉得我对问题的感觉更好。我很确定有些东西不匹配,我只是没有弄清楚究竟是什么。 – Alan
虽然这应该是其中的一部分。这是一个非常确定性的过程,对不一致的容忍度为零。根据您实际发送的请求,错误消息中的CanonicalRequest将间接地告诉您应该将哪些参数传递给getSignedUrl()。 –
谢谢,终于找到了正确的标题,并取得了成功的放入请求。 – Alan