S3预签名URL文件上传 – nodeJS + 客户端

huangapple go评论70阅读模式
英文:

S3 presigned url file upload – nodeJS + client

问题

以下是您提供的代码的翻译:

**tl;dr** - 浏览器从服务器(AWS S3)获取的预签名 URL 发出 `OPTIONS` 请求,得到 `200` 响应,但接下来的 `PUT` 请求得到 `403` 响应。

这是我负责 AWS 和其 S3 服务的服务器端代码:

```javascript
const AWS = require('aws-sdk');
const config = {
  accessKeyId: process.env.AWS_ACCESS_KEY_ID,
  secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
  region: process.env.AWS_BUCKET_REGION,
};
AWS.config.update(config);

const S3Bucket = new AWS.S3({
  signatureVersion: 'v4',
  params: { Bucket: process.env.AWS_BUCKET_NAME },
});

const uploadImage = async (fileData) => {
  // fileData 包含 `name``type` 属性
  const Key = `images/${fileData.name}`;
  const params = {
    Bucket: process.env.AWS_BUCKET_NAME,
    Key,
    Expires: 15 * 60, // 15 分钟
    ContentType: fileData.type,
    ACL: 'public-read',
  };
  const url = await S3Bucket.getSignedUrlPromise('putObject', params);
  return url;
};

上述代码中的 IAM 用户(AWS_ACCESS_KEY_IDAWS_SECRET_ACCESS_KEY)附有以下策略:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "Stmt1418647210000",
            "Effect": "Allow",
            "Action": [
                "s3:Put*"
            ],
            "Resource": [
                "arn:aws:s3:::my-actual-bucket-name-here/*"
            ]
        }
    ]
}

我的实际存储桶没有设置 Bucket policy,并具有以下 CORS 配置:

<?xml version="1.0" encoding="UTF-8"?>
<CORSConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
<CORSRule>
    <AllowedOrigin>*</AllowedOrigin>
    <AllowedMethod>HEAD</AllowedMethod>
    <AllowedMethod>GET</AllowedMethod>
    <AllowedMethod>PUT</AllowedMethod>
    <AllowedMethod>POST</AllowedMethod>
    <MaxAgeSeconds>3000</MaxAgeSeconds>
    <AllowedHeader>*</AllowedHeader>
</CORSRule>
</CORSConfiguration>

当我从浏览器向服务器发出请求时,我会在响应中收到一个签名 URL。然后我使用以下代码执行 PUT 请求:

// `file` 是一个 File 类对象(继承自 `FilePrototype`,后者继承自 `BlobPrototype`,具有以下属性
// lastModified: 1556044130023
// name: "profile.jpg"
// size: 788956
// type: "image/jpeg"
// uid: "rc-upload-1578069253604-2"
// webkitRelativePath: ""

const url = 'https://my-actual-bucket.s3.eu-central-1.amazonaws.com/images/profile.jpg?Content-Type=image%2Fjpeg&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=MYCREDENTIALCODE%2F20200103%2Feu-central-1%2Fs3%2Faws4_request&X-Amz-Date=20200103T163418Z&X-Amz-Expires=900&X-Amz-Signature=b90ed9cbdfadc6401521f80f5d4a65e7d4182becd392ad274ffbe3405d626055&X-Amz-SignedHeaders=host%3Bx-amz-acl&x-amz-acl=public-read';
const response = await fetch(url, {
  method: 'PUT',
  body: file,
  headers: {
    'Content-Type': file.type,
  },
});

在我的网络选项卡中,我可以看到一个 OPTIONS 请求,最终返回 200,然后是一个后续的 PUT 请求,最终返回 403,其中显示:

<Error><Code>AccessDenied</Code><Message>Access Denied</Message><RequestId>BFBCD6D538B3EB6A</RequestId><HostId>BtQuuVJw63Ixvir1ghCu0QLq/FKORNSIyyIh9AoYhul1TnsaoZZ1V0p/FBooM/0HTNhM7ZSegM8=</HostId></Error>

这里有什么问题?您有什么想法?


<details>
<summary>英文:</summary>

**tl;dr** – `OPTIONS` request made from the browser to a presigned url acquired from a server (AWS S3) gets `200`, but the following `PUT` request get `403` 

This is my server-side code responsible for AWS and its S3 service:

const AWS = require('aws-sdk');
const config = {
accessKeyId: process.env.AWS_ACCESS_KEY_ID,
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
region: process.env.AWS_BUCKET_REGION,
};
AWS.config.update(config);

const S3Bucket = new AWS.S3({
signatureVersion: 'v4',
params: { Bucket: process.env.AWS_BUCKET_NAME },
});

const uploadImage = async (fileData) => {
// fileData contains name and type properties
const Key = images/${fileData.name};
const params = {
Bucket: process.env.AWS_BUCKET_NAME,
Key,
Expires: 15 * 60, // 15 minutes
ContentType: fileData.type,
ACL: 'public-read',
};
const url = await S3Bucket.getSignedUrlPromise('putObject', params);
return url;
};


The IAM User from the code above (`AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`) has the following policy attached:

{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "Stmt1418647210000",
"Effect": "Allow",
"Action": [
"s3:Put*"
],
"Resource": [
"arn:aws:s3:::my-actual-bucket-name-here/*"
]
}
]
}


My actual bucket has no `Bucket policy` set and has the following CORS configuration:

<?xml version="1.0" encoding="UTF-8"?>
<CORSConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
<CORSRule>
<AllowedOrigin></AllowedOrigin>
<AllowedMethod>HEAD</AllowedMethod>
<AllowedMethod>GET</AllowedMethod>
<AllowedMethod>PUT</AllowedMethod>
<AllowedMethod>POST</AllowedMethod>
<MaxAgeSeconds>3000</MaxAgeSeconds>
<AllowedHeader>
</AllowedHeader>
</CORSRule>
</CORSConfiguration>


When I make a request from the browser to my server I get a signed URL in the response. Then I use this URL to perform a put request using the following code:

// file is a File class object (that inherits from FilePrototype which inherits from BlobPrototype that has the following attributes
// lastModified: 1556044130023
​// name: "profile.jpg"
​​// size: 788956
​​// type: "image/jpeg"
​​// uid: "rc-upload-1578069253604-2"
​​// webkitRelativePath: ""

const url = 'https://my-actual-bucket.s3.eu-central-1.amazonaws.com/images/profile.jpg?Content-Type=image%2Fjpeg&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=MYCREDENTIALCODE%2F20200103%2Feu-central-1%2Fs3%2Faws4_request&X-Amz-Date=20200103T163418Z&X-Amz-Expires=900&X-Amz-Signature=b90ed9cbdfadc6401521f80f5d4a65e7d4182becd392ad274ffbe3405d626055&X-Amz-SignedHeaders=host%3Bx-amz-acl&x-amz-acl=public-read';
const response = await fetch(url, {
method: 'PUT',
body: file,
headers: {
'Content-Type': file.type,
},
});


In my Network tab I can see an `OPTIONS` request that ends up returning `200`, and a following `PUT` request that ends up with `403` saying:

<Error><Code>AccessDenied</Code><Message>Access Denied</Message><RequestId>BFBCD6D538B3EB6A</RequestId><HostId>BtQuuVJw63Ixvir1ghCu0QLq/FKORNSIyyIh9AoYhul1TnsaoZZ1V0p/FBooM/0HTNhM7ZSegM8=</HostId></Error>


Any idea what am I doing wrong here?

</details>


# 答案1
**得分**: 4

你需要将`S3:GetObject`权限添加到您的IAM策略中。这个权限是必需的,因为用户(在这种情况下是应用程序)最终是在通过预签名URL授予其他人"读取"(获取)对象的权限。因此,它还需要具有"读取"(获取)权限。

<details>
<summary>英文:</summary>

You need to add the `S3:GetObject` permission to your IAM policy. This permission is required because the user (in this case the application) is ultimately granting others permission to &quot;read&quot; (get) an object via a pre-signed URL. Therefore, it also needs to have the &quot;read&quot; (get) permission.

</details>



huangapple
  • 本文由 发表于 2020年1月4日 00:41:26
  • 转载请务必保留本文链接:https://go.coder-hub.com/59582186.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定