我正在使用NodeJs将文件上传到AWS S3。我希望客户端能够安全地下载文件。因此,我正在尝试生成签名的URL,这些URL在一次使用后会过期。我的代码如下:
上传
const s3bucket = new AWS.S3({
accessKeyId: 'my-access-key-id',
secretAccessKey: 'my-secret-access-key',
Bucket: 'my-bucket-name',
})
const uploadParams = {
Body: file.data,
Bucket: 'my-bucket-name',
ContentType: file.mimetype,
Key: `files/${file.name}`,
}
s3bucket.upload(uploadParams, function (err, data) {
// ...
})
下载
const url = s3bucket.getSignedUrl('getObject', {
Bucket: 'my-bucket-name',
Key: 'file-key',
Expires: 300,
})
问题
打开URL时,我得到以下信息:
This XML file does not appear to have any style information associated with it. The document tree is shown below.
<Error>
<Code>AccessDenied</Code>
<Message>
There were headers present in the request which were not signed
</Message>
<HeadersNotSigned>host</HeadersNotSigned>
<RequestId>D63C8ED4CD8F4E5F</RequestId>
<HostId>
9M0r2M3XkRU0JLn7cv5QN3S34G8mYZEy/v16c6JFRZSzDBa2UXaMLkHoyuN7YIt/LCPNnpQLmF4=
</HostId>
</Error>
我绝对找不到错误。我真的很感谢您的帮助:)
答案 0 :(得分:1)
您的代码正确,请仔细检查以下内容:
您的存储桶访问策略。
通过您的API密钥的存储桶权限。
您的API密钥和机密。
您的存储桶名称和密钥。
对于存储桶策略,您可以使用以下内容:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "PublicReadGetObject",
"Effect": "Allow",
"Principal": "*",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::bucket/*"
}
]
}
用您的存储桶名称更改存储桶。
对于用户和访问密钥权限(#2),您应该按照以下步骤操作:
1-转到AWS Identity and Access Management(IAM),然后单击“策略”链接,然后单击“创建策略”按钮。
2-选择“ JSON”标签。
3-输入以下语句,确保更改存储桶名称,然后单击“查看策略”按钮。
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "VisualEditor1",
"Effect": "Allow",
"Action": "s3:*",
"Resource": "arn:aws:s3:::YOURBUCKETNAME"
}
]
}
4-输入您的策略名称,然后点击“创建策略”按钮。
5-点击“用户”链接,找到您当前的用户名(您已经具有该用户名的访问密钥和机密)
6-点击“添加权限”按钮。
7-添加我们在上一步中创建的策略并保存。
最后,请确保您的存储桶不可从“公共”访问,将正确的内容类型添加到文件中并设置signatureVersion: 'v4'
最后的代码应该是这样,谢谢@Vaisakh PS:
const s3bucket = new AWS.S3({
signatureVersion: 'v4',
accessKeyId: 'my-access-key-id',
secretAccessKey: 'my-secret-access-key',
Bucket: 'my-bucket-name',
})
const uploadParams = {
Body: file.data,
Bucket: 'my-bucket-name',
ContentType: file.mimetype,
Key: `files/${file.name}`,
}
s3bucket.upload(uploadParams, function (err, data) {
// ...
})
const url = s3bucket.getSignedUrl('getObject', {
Bucket: 'my-bucket-name',
Key: 'file-key',
Expires: 300,
})
答案 1 :(得分:1)
您的代码看起来不错,但是我认为您在创建signatureVersion: 'v4'
对象时缺少了s3bucket
参数。请尝试以下更新的代码。
const s3bucket = new AWS.S3({
signatureVersion: 'v4',
accessKeyId: 'my-access-key-id',
secretAccessKey: 'my-secret-access-key',
Bucket: 'my-bucket-name',
})
const uploadParams = {
Body: file.data,
Bucket: 'my-bucket-name',
ContentType: file.mimetype,
Key: `files/${file.name}`,
}
s3bucket.upload(uploadParams, function (err, data) {
// ...
})
const url = s3bucket.getSignedUrl('getObject', {
Bucket: 'my-bucket-name',
Key: 'file-key',
Expires: 300,
})
有关signatureVersion: 'v4'
的更多信息,请参见以下链接
https://docs.aws.amazon.com/general/latest/gr/signature-version-4.html
https://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-streaming.html
您还可以尝试以下创建nodejs
的{{1}}库
答案 2 :(得分:1)
如果您的s3文件已加密,请确保您的策略还可以访问加密密钥和相关操作。
答案 3 :(得分:1)
当我在本地测试lambda函数的工作原理时,我遇到了同样的问题,但是在部署之后却无法正常工作。一旦我向lambda函数添加了s3完全访问权限它起作用。
答案 4 :(得分:1)
我在使用无服务器框架的应用程序中也遇到了这个问题。
我的修复是向 serverless.yml 文件中的 IAM 角色添加 S3 权限。
我不太确定 s3 是如何生成预签名 URL 的,但事实证明它们会将您的 IAM 角色考虑在内。
添加所有 s3 操作就成功了。这就是 S3 中 IAM 角色的样子?
iamRoleStatements:
- Effect: Allow
Action:
- 's3:*'
Resource:
- 'arn:aws:s3:::${self:custom.imageBucket}/*'
答案 5 :(得分:0)
我一直遇到类似的问题,但我的原因是地区设置。 在我们的后端,我们对该应用程序进行了一些配置设置。
其中一个是"region": "us-west-2"
,因此使用该区域创建了预签名的url,但是当在前端调用该URL时,该区域设置为"us-west-1"
。
更改为相同即可解决此问题。
答案 6 :(得分:0)
从最近创建的存储桶移动到最近创建的存储桶时,我最近看到了这个问题。
v2的预签名链接(目前)似乎仍然可以在旧的存储桶上运行,而新的存储桶必须使用v4。
修订后的计划– 2020年6月24日之后创建的任何新存储桶都不会 支持SigV2签名的请求,尽管现有存储桶将继续 支持SigV2,同时我们与客户合作,逐步淘汰旧版本 请求签名方法。
即使您可以继续在现有存储桶和SigV2上使用 支持SigV2的AWS区域的子集,我鼓励您 迁移到SigV4,获得了一些重要的安全性和效率 好处。
我们的解决方案涉及更新AWS开发工具包以默认使用它;我怀疑较新的版本可能已经默认了此设置。