如何使用AWS Web API和Lambda验证无服务器Web请求?

时间:2017-02-18 14:25:37

标签: python authentication aws-lambda aws-api-gateway

背景信息少,

我建立了一个互动网站,用户可以将图像上传到S3。我构建了它,因此使用签名请求(python django后端)将图像上传从浏览器直接传送到AWS S3。

现在的问题是,用户希望能够旋转图像。我同样我希望这样设置,以便用户的请求直接来自浏览器。我构建了一个AWS Lambda函数并将其附加到web api,它将接受POST请求。我一直在测试,我终于开始工作了。该函数需要2个输入keyrotate_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' }

一切正常。那么现在我的问题是如何为这个系统强制执行某种身份验证?每个映像的所有权详细信息都驻留在django Web服务器上。虽然所有图像都被视为“公开”,但我希望强制只允许每个图像的所有者旋转自己的图像。

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

1 个答案:

答案 0 :(得分:5)

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

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

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

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

步骤3:创建Cognito Identity Pool时,您可以定义允许用户访问哪些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}*"
      ]
    }
  ]
}

注意第二个块将“S3:GetObject”分配给S3存储桶中的所有文件;第三个块将“S3:PutObject”分配给前缀为用户的Cognito身份ID的文件。

步骤4:在前端JS中,从Cognito Identity Pool获取临时凭证

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();
    })
  ));
}

步骤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}`);
    })
  ));
}