尝试使用预签名 URL 上传到 Google 云存储时,访问被拒绝(SA 没有 storage.objects.create 访问权限)

尝试使用预签名 URL 上传到 Google 云存储时,访问被拒绝(SA 没有 storage.objects.create 访问权限)

尝试允许客户端通过预签名的 URL 上传文件时遇到问题。

收到错误

<?xml version='1.0' encoding='UTF-8'?>
<Error>
<Code>AccessDenied</Code>
<Message>Access denied.</Message>
<Details>
urlsigner@<project>.iam.gserviceaccount.com does not have storage.objects.create access to <bucket-name>/<filename>.pdf.
</Details>
</Error>

代码作为 gcp 云函数执行,用于生成。云函数是默认的服务帐户函数,已更新为 urlSigner

async createSignedUrl({
    contentType,
    fileName,
    userId,
  }: SignedUrlRequest): Promise<string> {
    const bucket = await this.cloudStorage.bucket(BUCKET_NAME);
    console.log(
      moment.utc().add('minutes', 15).toDate(),
      Date.now() + 15 * 60 * 1000
    );
    const options: any = {
      version: 'v4',
      action: 'write',
      expires: Date.now() + 15 * 60 * 1000,
      contentType,
    };
    const [signedUrl] = await bucket
      .file(`${userId}-${fileName}`)
      .getSignedUrl(options);

    return signedUrl;
  }

CORS 临时 json 文件

[
  {
    "maxAgeSeconds": 3600,
    "method": ["GET", "POST", "PUT", "DELETE", "OPTIONS"],
    "origin": ["*"],
    "responseHeader": [
      "Content-Type",
      "Authorization",
      "Content-Length",
      "User-Agent",
      "x-goog-resumable"
    ]
  }
]

创建 GCP 资源的步骤

gcloud iam service-accounts create urlsigner --display-name="GCS URL Signer" --project=<project-id>

gcloud iam service-accounts keys create service_account.json --iam-account=urlsigner@<project-id>.iam.gserviceaccount.com

gsutil mb gs://<bucket-name>

gsutil iam ch serviceAccount:urlsigner@<project-id>.iam.gserviceaccount.com:roles/storage.admin gs://<bucket-name>

gsutil cors set cors-json-file.json gs://<bucket-name>

上传代码

export async function uploadDoc(preSignedUrl: string, file: File) {
  return await new Promise((resolve, reject) => {
    const xhr = new XMLHttpRequest();
    xhr.open('PUT', preSignedUrl, true);
    xhr.onload = () => {
      const status = xhr.status;
      if (status === 200) {
        console.log('uploaded');
        resolve('good to go');
      } else {
        console.log('failed to upload');
        reject('not good to go');
      }
    };

    xhr.onabort = () => {
      reject('aborted for idk');
    };

    xhr.onerror = () => {
      reject('Failed for idk');
    };

    xhr.setRequestHeader('Content-Type', file.type);
    const formData = new FormData();
    formData.append('verify-doc', file);
    xhr.send(formData);
  });
}

最后,验证云函数是否具有对发送回前端的 blob 进行签名所需的 Service-Token-Creator 角色,以及在存储桶权限下,服务帐户是否也列出了存储管理员角色。所以现在我只是在挠头,为什么我会收到由于服务帐户缺乏权限而被拒绝访问的错误。

在此先向所有能提供帮助的人表示感谢。

附言:我也参考了这篇文章https://medium.com/google-cloud/upload-download-files-from-a-browser-with-gcs-signed-urls-and-signed-policy-documents-f66fff8e425

答案1

您收到的错误意味着您的云函数服务帐户缺少权限storage.objects.create

为了解决这个问题,你可以给你的服务帐号预定义角色喜欢存储对象创建者或创建一个自定义角色获得该许可。


编辑

我发现我错过了你的服务帐户有存储管理员角色,包括存储.对象。* 权限因此应该足够了。

我建议您尝试以下选项:

  1. 去除存储管理员从您函数的服务帐户中移除角色,然后再次添加。请确保等待几分钟,以便角色能够正确传播。
  2. 如果第一个选项不适合您,请尝试为该函数的服务帐号提供存储对象创建者服务帐户令牌创建者角色分别。

如果有帮助的话请告诉我。

相关内容