如何使用AWS Web API和Lambda对无服务器Web请求进行身份验证?

jef*_*ind 3 python authentication aws-lambda aws-api-gateway

一点背景信息,

我建立了一个交互式网站,用户可以在其中将图像上传到S3。我构建了它,以便使用签名的请求(python django backend)将图像从浏览器直接上传到AWS S3。

现在的问题是,用户希望能够旋转图像。同样,我希望进行此设置,以便用户的请求直接来自浏览器。我构建了一个AWS Lambda函数并将其附加到一个Web api,它将接受POST请求。我一直在测试,终于使它工作了。该函数采用2个输入,key以及rotate_direction,它们作为POST变量到web API通过。它们进入event变量中的python函数。这是简单的Lambda函数:

from __future__ import print_function
import boto3
import os
import sys
import uuid
from PIL import Image

s3_client = boto3.client('s3')

def rotate_image(image_path, upload_path, rotate_direction):
    with Image.open(image_path) as image:
        if rotate_direction == "right":
            image.rotate(-90).save(upload_path)
        else:
            image.rotate(90).save(upload_path)

def handler(event, context):
    bucket = 'the-s3-bucket-name'
    key = event['key']
    rotate_direction = event['rotate_direction']
    download_path = '/tmp/{}{}'.format(uuid.uuid4(), key)
    upload_path = '/tmp/rotated_small-{}'.format(key)


    s3_client.download_file(bucket, key, download_path)
    rotate_image(download_path, upload_path, rotate_direction)
    s3_client.delete_object(Bucket=bucket, Key=key)
    s3_client.upload_file(upload_path, bucket, key)

    return { 'message':'rotated' }
Run Code Online (Sandbox Code Playgroud)

一切正常。所以现在我的问题是如何对该系统实施某种身份验证?有关每个图像的所有权详细信息位于django Web服务器上。虽然所有图像都被认为是“公开的”,但我希望强制每个图像的所有者只能旋转其自己的图像。

通过这个项目,我一直在通过直接从浏览器发出内容请求进入新的领域。我可以理解如何仅通过从Web服务器发出POST请求来控制访问,在Web服务器中可以验证图像的所有权。请求是否仍可能来自浏览器?

小智 5

TL; DR解决方案:创建一个Cognito身份池,分配策略的用户只能上传以其Identity ID为前缀的文件。

如果我正确理解了您的问题,则想为存储在S3上的图像设置一种公开可见的方式,但只能由上传它的用户编辑。实际上,您无需浏览Lambda函数就可以在浏览器中验证文件所有权,旋转图像并将旋转的图像上传到S3。

步骤1:创建Cognito用户池以创建用户目录。如果您已经具有用户登录/注册身份验证系统,则可以跳过此步骤。

步骤2:创建一个Cognito身份池以启用联盟身份,以便您的用户可以从身份池中获得临时AWS凭证,并使用它将文件上传到S3,而无需通过服务器/ lambda。

第3步:在创建Cognito身份游泳池,您可以定义什么S3资源的用户被允许访问的策略。这是一个示例政策

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "mobileanalytics:PutEvents",
        "cognito-sync:*",
        "cognito-identity:*"
      ],
      "Resource": [
        "*"
      ]
    },
    {
      "Effect": "Allow",
      "Action": [
        "s3:GetObject"
      ],
      "Resource": [
        "arn:aws:s3:::YOUR_S3_UPLOADS_BUCKET_NAME/*"
      ]
    },
    {
      "Effect": "Allow",
      "Action": [
        "s3:PutObject"
      ],
      "Resource": [
        "arn:aws:s3:::YOUR_S3_UPLOADS_BUCKET_NAME/${cognito-identity.amazonaws.com:sub}*"
      ]
    }
  ]
}
Run Code Online (Sandbox Code Playgroud)

请注意,第二个块将“ S3:GetObject”分配给S3存储桶中的所有文件;和第三块受让人“S3:PutObject”到文件只有前缀与用户的Cognito标识ID。

步骤4:在前端JS中,从Cognito身份池获取临时证书

export function getAwsCredentials(userToken) {
  const authenticator = `cognito-idp.${config.cognito.REGION}.amazonaws.com/${config.cognito.USER_POOL_ID}`;

  AWS.config.update({ region: config.cognito.REGION });

  AWS.config.credentials = new AWS.CognitoIdentityCredentials({
    IdentityPoolId: config.cognito.IDENTITY_POOL_ID,
    Logins: {
      [authenticator]: userToken
    }
  });

  return new Promise((resolve, reject) => (
    AWS.config.credentials.get((err) => {
      if (err) {
        reject(err);
        return;
      }

      resolve();
    })
  ));
}
Run Code Online (Sandbox Code Playgroud)

步骤5:使用凭据将文件上传到S3,并在文件名前添加用户的Cognito身份ID。

export async function s3Upload(file, userToken) {
  await getAwsCredentials(userToken);

  const s3 = new AWS.S3({
    params: {
      Bucket: config.s3.BUCKET,
    }
  });
  const filename = `${AWS.config.credentials.identityId}-${Date.now()}-${file.name}`;

  return new Promise((resolve, reject) => (
    s3.putObject({
      Key: filename,
      Body: file,
      ContentType: file.type,
      ACL: 'public-read',
    },
    (error, result) => {
      if (error) {
        reject(error);
        return;
      }

      resolve(`${config.s3.DOMAIN}/${config.s3.BUCKET}/${filename}`);
    })
  ));
}
Run Code Online (Sandbox Code Playgroud)