微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

试图从 Lambda 获取图像,但访问被拒绝使用 Amplify 客户端库运行良好

如何解决试图从 Lambda 获取图像,但访问被拒绝使用 Amplify 客户端库运行良好

我使用无服务器框架创建了 S3 Bucket,如下所示:

AssetsBucket:
  Type: AWS::S3::Bucket
  DeletionPolicy: Retain
  Properties:
    CorsConfiguration:
      CorsRules:
        - AllowedMethods:
            - GET
            - HEAD
            - PUT
          AllowedOrigins:
            - '*'
          AllowedHeaders:
            - '*'
          ExposedHeaders:
            - 'x-amz-server-side-encryption'
            - 'x-amz-request-id'
            - 'x-amz-id-2'
            - 'ETag'
          MaxAge: 3000

然后,我创建了一个身份池并定义了我需要的角色:

IdentityPool:
  Type: AWS::Cognito::IdentityPool
  Properties:
    AllowUnauthenticatedIdentities: true
    CognitoIdentityProviders:
      - ClientId: !Ref WebUserPoolClient
        ProviderName: !GetAtt CognitoUserPool.ProviderName

CognitoAuthorizedRole:
  Type: AWS::IAM::Role
  Properties:
    AssumeRolePolicyDocument:
      Version: "2012-10-17"
      Statement:
        - Effect: Allow
          Principal:
            Federated: "cognito-identity.amazonaws.com"
          Action:
            - sts:AssumeRoleWithWebIdentity
          Condition:
            StringEquals:
              "cognito-identity.amazonaws.com:aud": !Ref IdentityPool
            ForAnyValue:StringLike:
              "cognito-identity.amazonaws.com:amr": authenticated

CognitoUnAuthorizedRole:
  Type: AWS::IAM::Role
  Properties:
    AssumeRolePolicyDocument:
      Version: "2012-10-17"
      Statement:
        - Effect: Allow
          Principal:
            Federated: "cognito-identity.amazonaws.com"
          Action:
            - sts:AssumeRoleWithWebIdentity
          Condition:
            StringEquals:
              "cognito-identity.amazonaws.com:aud": !Ref IdentityPool
            ForAnyValue:StringLike:
              "cognito-identity.amazonaws.com:amr": unauthenticated

IdentityPoolRoleMapping:
  Type: AWS::Cognito::IdentityPoolRoleAttachment
  Properties:
    IdentityPoolId: !Ref IdentityPool
    Roles:
      authenticated: !GetAtt CognitoAuthorizedRole.Arn
      unauthenticated: !GetAtt CognitoUnAuthorizedRole.Arn

这些角色有以下规则:

认知授权角色:

{
    "Version": "2012-10-17","Statement": [
        {
            "Condition": {
                "StringLike": {
                    "s3:prefix": [
                        "public/","public/*","protected/","protected/*","private/${cognito-identity.amazonaws.com:sub}/","private/${cognito-identity.amazonaws.com:sub}/*"
                    ]
                }
            },"Action": [
                "s3:ListBucket"
            ],"Resource": [
                "arn:aws:s3:::MY_BUCKET_HERE"
            ],"Effect": "Allow"
        },{
            "Action": [
                "s3:Getobject","s3:PutObject"
            ],"Resource": [
                "arn:aws:s3:::MY_BUCKET_HERE/uploads/*","arn:aws:s3:::MY_BUCKET_HERE/public/*","arn:aws:s3:::MY_BUCKET_HERE/protected/${cognito-identity.amazonaws.com:sub}/*","arn:aws:s3:::MY_BUCKET_HERE/private/${cognito-identity.amazonaws.com:sub}/*"
            ],{
            "Action": [
                "s3:Getobject"
            ],"Resource": [
                "arn:aws:s3:::MY_BUCKET_HERE/protected/*"
            ],"Effect": "Allow"
        }
    ]
}

CognitoUnAuthorizedRole:

{
    "Version": "2012-10-17","protected/*"
                    ]
                }
            },"Resource": [
                "arn:aws:s3:::MY_BUCKET_HERE/public/*","arn:aws:s3:::MY_BUCKET_HERE/protected/*"
            ],"Effect": "Allow"
        }
    ]
}

所以,这是我的问题:

如果我使用带有放大库的 Storage.get 方法调用对象,我会毫无问题地获得图像。

但是,如果我在我的 LAMBDA 中这样做:

const { S3Client,GetobjectCommand } = require('@aws-sdk/client-s3');
const { getSignedUrl } = require('@aws-sdk/s3-request-presigner');

const s3Client = new S3Client();

const command = new GetobjectCommand(params);
const url = await getSignedUrl(s3Client,command,{ expiresIn: 3600 });

return url;

lambda 返回 URL,但是当我从客户端获取此 URL 时,我收到拒绝访问错误

MyLambda:
    handler: functions/My_Lambda.handler
    environment:
      BUCKET: !Ref AssetsBucket
    iamRoleStatements:
      - Effect: Allow
        Action: s3:Getobject
        Resource: !GetAtt AssetsBucket.Arn

我不知道为什么会出现此错误...对于客户端,该库运行良好,但是如果我从 Lambda 尝试类似的操作,则它不起作用...

以下是有关标题的更多信息:

来自正确图像的标题(Storage.get 方法):

Response-headers:

HTTP/1.1 200 OK
x-amz-id-2: HERE_THE_VALUE
x-amz-request-id: HERE_THE_VALUE
Date: Sat,17 Jul 2021 21:10:06 GMT
Last-Modified: Sun,11 Jul 2021 19:28:42 GMT
ETag: "HERE_THE_VALUE"
Accept-Ranges: bytes
Content-Type: application/octet-stream
Server: AmazonS3
Content-Length: 1338


Request-headers:

GET /public/menu_icons/ADMINISTradOR.svg?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Content-Sha256=UNSIGNED-PAYLOAD&X-Amz-Credential=ASIA4YRKNIM2VGLZX7UR%2F20210717%2Fus-east-2%2Fs3%2Faws4_request&X-Amz-Date=20210717T211004Z&X-Amz-Expires=900&X-Amz-Security-Token=IQoJ... HTTP/1.1
Host: ecommerce-gateway-develop-assetsbucket-72ahiv6louu0.s3.us-east-2.amazonaws.com
Connection: keep-alive
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML,like Gecko) Chrome/91.0.4472.124 Safari/537.36
Accept: image/avif,image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8
Sec-GPC: 1
sec-fetch-site: cross-site
sec-fetch-mode: no-cors
Sec-Fetch-Dest: image
Referer: http://localhost:3000/
Accept-Encoding: gzip,deflate,br
Accept-Language: en-US,en;q=0.9

来自图像 LAMBDA 错误标题

Response-headers:

HTTP/1.1 403 Forbidden
x-amz-request-id: HERE_THE_VALUE
x-amz-id-2: HERE_THE_VALUE
Content-Type: application/xml
transfer-encoding: chunked
Date: Sat,17 Jul 2021 21:10:04 GMT
Server: AmazonS3

Request-headers:

GET /public/menu_icons/ADMINISTradOR.svg?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Content-Sha256=UNSIGNED-PAYLOAD&X-Amz-Credential=ASIA4YRKNIM2VU4GBIEB%2F20210717%2Fus-east-2%2Fs3%2Faws4_request&X-Amz-Date=20210717T211003Z&X-Amz-Expires=3600&X-Amz-Security-Token=IQoJ.... HTTP/1.1
Host: ecommerce-gateway-develop-assetsbucket-72ahiv6louu0.s3.us-east-2.amazonaws.com
Connection: keep-alive
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML,en;q=0.9

两个标头的某些部分是不同的...例如,LAMBDA 标头没有 ETag...我该怎么办?谢谢!

新更新

如果我从 lambda 的日志中获取 url,我会得到这个:

https://ecommerce-gateway-develop-assetsbucket-72ahiv6louu0.s3.us-east-2.amazonaws.com/public/menu_icons/REPORTES.svg?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Content-Sha256=UNSIGNED-PAYLOAD&X-Amz-Credential=xxxxxxIM22YARUSUJ%2F2xxxxx17%2Fus-east-2%2Fs3%2Faws4_request&X-Amz-Date=20210717T230538Z&X-Amz-Expires=3600&X-Amz-Signature=0a37f13e2a7axxxxf4d38cebxxxxxxxxx557c8805057ac6ddffe71c&X-Amz-SignedHeaders=host&x-id=Getobject

哪个效果很好。

然后,如果我转到网页并使用 Amplify 中的 Auth.SignIn 方法用户身份登录,然后执行 Storage.get:

https://ecommerce-gateway-develop-assetsbucket-72ahiv6louu0.s3.us-east-2.amazonaws.com/public/menu_icons/ADMINISTRADOR.svg?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Content-Sha256=UNSIGNED-PAYLOAD&X-Amz-Credential=ASIA4YxxxxxxxxxxF20210717%2Fus-east-2%2Fs3%2Faws4_request&X-Amz-Date=20210717T230825Z&X-Amz-Expires=900&X-Amz-Security-Token=IQoJb3JpZ2luX2VjEG8aCXVzLWVhc3QtMiJHMEUCIE3FrI0oFvpEIxxxxxxxxdm1bc&X-Amz-Signature=716642f08xxxxxxxxxx988e954b0335a2e04df44e7&X-Amz-SignedHeaders=host&x-amz-user-agent=aws-sdk-js%2F3.6.1%20os%2FLinux%20lang%2Fjs%20md%2Fbrowser%2FChrome_91.0.4472.124%20api%2Fs3%2F3.6.1%20aws-amplify%2F4.1.3_js&x-id=GetObject

也很好用。

但是,如果我调用 lambda,现在 url 会更改:

https://ecommerce-gateway-develop-assetsbucket-72ahiv6louu0.s3.us-east-2.amazonaws.com/public/menu_icons/ADMINISTRADOR.svg?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Content-Sha256=UNSIGNED-PAYLOAD&X-Amz-Credential=ASIA4xxxxxxxxxNGRZ7U%2F20210717%2Fus-east-2%2Fs3%2Faws4_request&X-Amz-Date=20210717T230824Z&X-Amz-Expires=3600&X-Amz-Security-Token=IQoJb3JpZ2luX2VjExxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxtZmeLenE30kHskEkfe4%3D&X-Amz-Signature=b9a400dxxxxxxxxx047445657919ff3b993b893fcbb3&X-Amz-SignedHeaders=host&x-id=GetObject

在这个网址有更多的参数...

这是我正在做的一个例子:https://github.com/MontoyaAndres/test_problem_s3_cognito

解决方法

我对您对现有系统的解释感到困惑(抱歉!),但一般方法是以下之一:

使用 Cognito

您的后端可以使用 Cognito 对用户进行身份验证,然后使用 AssumeRoleWithWebIdentity 返回一组凭据。然后,用户的客户端可以使用这些凭证根据分配的权限直接访问 AWS 服务

例如,他们可能被允许访问自己在 Amazon S3 存储桶中的子目录,或从特定的 DynamoDB 表中读取。这可以通过将请求直接发送到 AWS 而不是通过后端来完成。

使用预签名网址

如果您的目标纯粹是授予对 Amazon S3 中私有对象的访问权限,那么而不是使用 Cognito,您的后端可以生成 Amazon S3 pre-signed URLs,提供对私有对象的限时访问对象。

每当后端生成包含对私有对象的引用(例如通过 <img src=...> 标记)的页面时,它可以执行以下操作:

  • 应用通过检查应用数据库中的信息来验证用户是否有权访问私有对象
  • 如果用户有权访问私有对象,后端会生成一个预签名的 URL
  • 预签名 URL 在 HTML 页面中返回(甚至作为直接链接)
  • 当 S3 收到预签名 URL 时,它会验证签名,如果正确,则返回私有对象

这种方法的好处是应用程序可以确定对单个对象的细粒度访问,而不是简单地使用存储桶和前缀来定义访问。这在用户之间基于每个对象共享数据(例如,用户可以与其他用户共享照片的照片共享应用)的情况下非常有用。

不要混用

在查看您的代码示例时,您的 Cognito 角色似乎授予对 S3 存储桶特定部分的访问权限:

arn:aws:s3:::MY_BUCKET_HERE/protected/${cognito-identity.amazonaws.com:sub}/*

然后,客户端可以使用其 Cognito 相关凭证直接访问存储桶的该部分。无需生成预签名网址。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。