繁体   English   中英

通过浏览器将 object 放在 AWS S3 上时出现意外错误 (putObject)

[英]Unexpected error putting object on AWS S3 through browser (putObject)

我通过我的 Node/Express 后端成功获得了一个预签名的 URL,用于 S3 上的putObject请求,并尝试通过浏览器上传相关文件。

当我通过浏览器(或邮递员)发出put请求时,我得到一个400403 我是 S3 的新手,不知道在哪里可以查找有关此的信息。

我知道如何正确获取预签名的 URL,但是为什么我对文件的put请求我得到了 URL 失败?

据我所知,我收到请求的存储桶是可公开访问的。

我查看了不同的代码演练教程、有关 S3 的文档以及此处的帖子。

我在前端请求预签名 URL 的代码

// Returns null or array of objects of type { url : string | null; contentType: string; }
const filesDescription = fileList[+key];

if (filesDescription === undefined || filesDescription === null) continue;

const Key          = `${prefix}/${filesDescription.name}`;
const ContentType  =  filesDescription.type;

const request  =  generateEndpointConfig(APIEndpoints.GET_PRESIGNED_URL, { Key, ContentType, userID });

const res      =  await apiCall(request.METHOD, request.URL, request.DATA);

const url = typeof res.data.signedUrl === "string" ? res.data.signedUrl : null;

presignedUrls.push({url, contentType:  ContentType});

我的 Node/Express 后端获取 URL 的代码

const { ContentType, Key } = req.body;

const s3 = new S3({
                accessKeyId: AWS_ACCESS_ID,
                secretAccessKey: AWS_SECRET
            });

const url = await s3.getSignedUrlPromise("putObject",
                {
                    Bucket: AMAZON_AWS_STATIC_BUCKET,
                    ContentType,
                    Key,
                    Expires: 300
                });

return res.status(StatusCodes.OK).json({ signedUrl: url })

最后,我提出的上传文件的put请求

const presignedUrls = await getPresignedUrls(+idx);
if (presignedUrls === null) return false;

for (const fileIdx in presignedUrls)
{
  const fileList      =  files[idx];
  const uploadConfig  =  presignedUrls[fileIdx];

  if (fileList === null || uploadConfig.url === null) continue;

  const fileToUpload = fileList[fileIdx];

  try
  {
    // Make put request for corresponding file to cloud service
    await axios.put(uploadConfig.url, fileToUpload,
    {
        headers:
        {
            "Content-Type": uploadConfig.contentType
        }
    });
  }

  catch(err)
  {
    return false;
  }

我还应该提一下,因为我是为经过身份验证的用户执行此操作,所以我还传输了Bearer <TOKEN>形式的Authorization header 。

=== 编辑 ===

这是我在Postman中返回状态码 403 的示例错误响应。

<?xml version="1.0" encoding="UTF-8"?>
<Error>
    <Code>AccessDenied</Code>
    <Message>Access Denied</Message>
    <RequestId>...</RequestId>
    <HostId>...</HostId>
</Error>

=== 编辑 2 ===

我的存储桶上的政策是

{
    "Version": "2012-10-17",
    "Id": "<POLICY_ID>",
    "Statement": [
        {
            "Sid": "<STMT_ID>",
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::cloudfront:user/CloudFront Origin Access Identity <CLOUDFRONT_ORIGIN_ID>"
            },
            "Action": "s3:GetObject",
            "Resource": "arn:aws:s3:::<S3_BUCKET_NAME>/*"
        },
        {
            "Sid": "<STMT_ID>",
            "Effect": "Allow",
            "Principal": {
                "Federated": [
                    "http://localhost:3000",
                    "https://www.my-website.com/"
                ],
            "Action": "s3:PutObject",
            "Resource": "arn:aws:s3:::<S3_BUCKET_NAME>/*"
        }
    ]
}

我存储桶上的 CORS 配置是

[
    {
        "AllowedHeaders": [
            "*"
        ],
        "AllowedMethods": [
            "PUT",
            "POST",
            "DELETE"
        ],
        "AllowedOrigins": [
            "https://www.my-website.com",
            "http://localhost:3000"
        ],
        "ExposeHeaders": []
    }
]

作为参考,此存储桶为 CDN 网络提供 CloudFront 分配,并且该存储桶仅在域级别的 GET 请求 go 时受到限制。

=== 编辑 3 ===

这是我尝试在浏览器上上传时返回的错误。

<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>...</AWSAccessKeyId>
<StringToSign>GET [A_LONG_NUMBER] /[BUCKET_NAME]/[PREFIX]/[IMAGE_NAME].jpg</StringToSign>
<SignatureProvided>...</SignatureProvided>
<StringToSignBytes>[SOME_BYTES]</StringToSignBytes>
<RequestId>...</RequestId>
<HostId>...</HostId>
</Error>

=== 最终编辑 ===

除了@jarmod 的回答之外,问题是一旦站点上的用户通过身份验证,我的应用程序配置会自动将Authorization header 转发给所有请求。 删除此 header 以调用 S3 使该过程在浏览器上成功运行。

您的 S3 存储桶上是否有 CORS 配置设置? 这指定了哪些源可以访问您的存储桶,以及它们可以使用哪些 HTTP 方法。 在这种情况下,您通常会在开发人员控制台中看到一个错误,看起来像Access to fetch at 'YOUR-PRESIGNED-URL' from origin 'YOUR-ORIGIN' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource

本页介绍了基本配置: https://docs.aws.amazon.com/AmazonS3/latest/dev/cors.html

当您最初签署 putObject URL 时,您提供了 IAM 用户的 AWS 凭证(访问密钥和秘密密钥)。 稍后,当 S3 预签名的 URL 呈现给 S3 时,该 URL 的原始签名者,即您的 IAM 用户,将成为上传的主体。 因此,该 IAM 用户委托人需要允许 s3:PutObject 在相关资源上的 IAM 策略,或者您的存储桶策略需要允许 IAM 用户委托人这样做。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM