处理从 lambda 到 cloudfront 或 s3 的 Cors

问题描述 投票:0回答:1

我正在尝试创建一个身份验证服务,其中我有一个 lambda,它使用 keycloak 通过密码授予来对用户进行身份验证。该服务由 s3 上的 React 前端主机组成,并通过 cloudfront 访问。前端应接收重定向 uri,并将其传递给 auth lambda 以对用户进行身份验证,并使用 301 重定向将其重定向到重定向 uri。我遇到的问题是当我发送 301 重定向时出现 cors 错误

 Access to XMLHttpRequest at 'https://jwt.io/' (redirected from 'https://z0kj9kfzwk.execute-api.us-west-2.amazonaws.com/dev/login') from origin 'https://auth-website-harmless-wren.s3.us-west-2.amazonaws.com' 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.

这是我的地形设置:

API网关

resource "aws_apigatewayv2_api" "auth_api_gw" {
  name          = "auth-api"
  protocol_type = "HTTP"

  cors_configuration {
    allow_headers = ["*"]
    allow_methods = ["POST", "OPTIONS"]
    allow_origins = ["*"]
    max_age       = 3000
  }
}

resource "aws_cloudwatch_log_group" "auth_api_gw_log_group" {
  name = "/aws/api-gw/${aws_apigatewayv2_api.auth_api_gw.name}"

  retention_in_days = 30
}

resource "aws_apigatewayv2_stage" "auth_api_gw_dev_stage" {
  api_id = aws_apigatewayv2_api.auth_api_gw.id

  name        = "dev"
  auto_deploy = true

  access_log_settings {
    destination_arn = aws_cloudwatch_log_group.auth_api_gw_log_group.arn

    format = jsonencode({
      requestId               = "$context.requestId"
      sourceIp                = "$context.identity.sourceIp"
      requestTime             = "$context.requestTime"
      protocol                = "$context.protocol"
      httpMethod              = "$context.httpMethod"
      resourcePath            = "$context.resourcePath"
      routeKey                = "$context.routeKey"
      status                  = "$context.status"
      responseLength          = "$context.responseLength"
      integrationErrorMessage = "$context.integrationErrorMessage"
      }
    )
  }
}

resource "aws_apigatewayv2_integration" "auth_api_gw_handler" {
  api_id = aws_apigatewayv2_api.auth_api_gw.id

  integration_type = "AWS_PROXY"
  integration_uri  = var.auth_lambda_invoke_arn
}
resource "aws_apigatewayv2_route" "auth_login_route" {
  api_id    = aws_apigatewayv2_api.auth_api_gw.id
  route_key = "POST /login"

  target = "integrations/${aws_apigatewayv2_integration.auth_api_gw_handler.id}"
}

resource "aws_apigatewayv2_route" "auth_register_route" {
  api_id    = aws_apigatewayv2_api.auth_api_gw.id
  route_key = "POST /register"

  target = "integrations/${aws_apigatewayv2_integration.auth_api_gw_handler.id}"
}

resource "aws_lambda_permission" "auth_api_gw_lambda_permission" {
  statement_id  = "AllowExecutionFromAPIGateway"
  action        = "lambda:InvokeFunction"
  function_name = var.auth_lambda_function_name
  principal     = "apigateway.amazonaws.com"

  source_arn = "${aws_apigatewayv2_api.auth_api_gw.execution_arn}/*/*"
}

拉姆达

locals {
  timestamp_suffix = timestamp()
}

resource "null_resource" "auth_lambda_package_build" {
  triggers = {
    always_run = local.timestamp_suffix
  }
  provisioner "local-exec" {
    command = <<EOT
      SOURCE_DIR="${path.root}"
      BACKEND_SOURCE_DIR="${path.module}/../../../../packages/backend"

      cd $SOURCE_DIR 
      pnpm --filter @auth/backend install
      pnpm --filter @auth/backend run build

      cp "$BACKEND_SOURCE_DIR/package.json" "$BACKEND_SOURCE_DIR/build"
      cp "$BACKEND_SOURCE_DIR/package-lock.json" "$BACKEND_SOURCE_DIR/build"

      cd "$BACKEND_SOURCE_DIR/build"
      npm ci --production

    EOT
  }

}

data "archive_file" "archive_auth_lambda" {
  type        = "zip"
  source_dir  = "${path.module}/../../../../packages/backend/build"
  output_path = "${path.module}/../../../../packages/backend/auth-lambda_${local.timestamp_suffix}.zip"

  depends_on = [ null_resource.auth_lambda_package_build ]
}

resource "random_pet" "lambda_bucket_name" {
  prefix = "lambda"
  length = 2
}

resource "aws_s3_bucket" "lambda_bucket" {
  bucket        = random_pet.lambda_bucket_name.id
  force_destroy = true
}

resource "aws_s3_bucket_public_access_block" "lambda_bucket" {
  bucket = aws_s3_bucket.lambda_bucket.id

  block_public_acls       = true
  block_public_policy     = true
  ignore_public_acls      = true
  restrict_public_buckets = true
}

resource "aws_s3_object" "auth_lambda_code_s3_object" {
  bucket = aws_s3_bucket.lambda_bucket.id
  key    = "auth-lambda.zip"
  source = data.archive_file.archive_auth_lambda.output_path
  etag   = filemd5(data.archive_file.archive_auth_lambda.output_path)
}

resource "aws_cloudwatch_log_group" "lambda_logs" {
  name = "/aws/lambda/${aws_lambda_function.auth_lambda_function.function_name}"

  retention_in_days = 30
}

resource "aws_iam_role" "auth_lambda_exec" {
  name = "auth_lambda_exec_role"

  assume_role_policy = jsonencode({
    Version = "2012-10-17"
    Statement = [{
      Action = "sts:AssumeRole"
      Effect = "Allow"
      Sid    = ""
      Principal = {
        Service = "lambda.amazonaws.com"
      }
      }
    ]
  })
}

resource "aws_iam_role_policy_attachment" "auth_lambda_policy" {
  role       = aws_iam_role.auth_lambda_exec.name
  policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
}


resource "aws_lambda_function" "auth_lambda_function" {
  function_name = "auth"

  s3_bucket = aws_s3_bucket.lambda_bucket.id
  s3_key    = aws_s3_object.auth_lambda_code_s3_object.key

  source_code_hash = data.archive_file.archive_auth_lambda.output_base64sha256

  runtime = "nodejs20.x"
  handler = "handler.handler"

  memory_size = 1024
  timeout     = 60

  role = aws_iam_role.auth_lambda_exec.arn
  environment {
    variables = {
      keycloak_auth_server_url     = var.keycloak_auth_server_url
      keycloak_realm               = var.keycloak_realm
      keycloak_client_id           = var.keycloak_client_id
      keycloak_client_secret       = var.keycloak_client_secret
      keycloak_admin_client_id     = var.keycloak_admin_client_id
      keycloak_admin_client_secret = var.keycloak_admin_client_secret
    }
  }
}

云前线

resource "aws_cloudfront_origin_access_identity" "auth_oai" {
  comment = "auth-website OAI"
}

resource "aws_cloudfront_distribution" "auth_distribution" {
  origin {
    domain_name = var.auth_bucket_regional_domain_name
    origin_id   = "auth-origin"

    s3_origin_config {
      origin_access_identity = aws_cloudfront_origin_access_identity.auth_oai.cloudfront_access_identity_path
    }
  }

  enabled         = true
  is_ipv6_enabled = true

  default_root_object = "index.html"

  restrictions {
    geo_restriction {
      restriction_type = "none"
    }
  }

  viewer_certificate {
    cloudfront_default_certificate = true
  }

  default_cache_behavior {

    allowed_methods  = ["GET", "HEAD", "OPTIONS"]
    cached_methods   = ["GET", "HEAD"]
    target_origin_id = "auth-origin"

    default_ttl = 3600
    min_ttl     = 0
    max_ttl     = 86400
    compress    = true

    forwarded_values {
      query_string = false
      cookies {
        forward = "none"
      }
    }

    viewer_protocol_policy = "redirect-to-https"

  }

  ordered_cache_behavior {
    path_pattern     = "./index.html"
    allowed_methods  = ["GET", "HEAD", "OPTIONS"]
    cached_methods   = ["GET", "HEAD", "OPTIONS"]
    target_origin_id = "auth-origin"

    default_ttl = 3600
    min_ttl     = 0
    max_ttl     = 86400
    compress    = true

    forwarded_values {
      query_string = false
      cookies {
        forward = "none"
      }
    }

    viewer_protocol_policy = "redirect-to-https"
  }


}

S3桶

resource "random_pet" "auth_website_bucket_name" {
  prefix = "auth-website"
  length = 2
}

resource "aws_s3_bucket" "auth_website_bucket" {
  bucket        = random_pet.auth_website_bucket_name.id
  force_destroy = true
}

resource "aws_s3_bucket_website_configuration" "auth_website_code_s3_configuration" {
  bucket = aws_s3_bucket.auth_website_bucket.id
  index_document {
    suffix = "index.html"
  }
  error_document {
    key = "index.html"
  }
}

resource "aws_s3_bucket_policy" "auth_website_bucket_policy" {

  depends_on = [aws_s3_bucket_acl.auth_website_bucket_acl]

  bucket = aws_s3_bucket.auth_website_bucket.id

  policy = jsonencode({
    Version = "2012-10-17"
    Statement = [{
      Sid       = "PublicReadGetObject"
      Effect    = "Allow"
      Principal = "*"
      Action    = "s3:GetObject",
      Resource  = "${aws_s3_bucket.auth_website_bucket.arn}/*",
    }]
  })
}

resource "aws_s3_bucket_public_access_block" "auth_website_bucket_public_access_block" {
  bucket                  = aws_s3_bucket.auth_website_bucket.id
  block_public_acls       = false
  block_public_policy     = false
  ignore_public_acls      = false
  restrict_public_buckets = false
}

resource "aws_s3_bucket_ownership_controls" "auth_website_ownership_controls" {
  bucket = aws_s3_bucket.auth_website_bucket.id

  rule {
    object_ownership = "BucketOwnerPreferred"
  }
}

resource "aws_s3_bucket_acl" "auth_website_bucket_acl" {

  depends_on = [aws_s3_bucket_ownership_controls.auth_website_ownership_controls, aws_s3_bucket_public_access_block.auth_website_bucket_public_access_block]

  bucket = aws_s3_bucket.auth_website_bucket.id
  acl    = "public-read"
}

locals {
  timestamp_suffix = timestamp()
}
resource "null_resource" "auth_website_package_build" {

  depends_on = [ aws_s3_bucket.auth_website_bucket ]

  triggers = {
    always_run = local.timestamp_suffix
  }
  provisioner "local-exec" {
    command = <<EOT
      ROOT_DIR="../"
      FRONTEND_DIR="$ROOT_DIR/packages/frontend"

      (cd $FRONTEND_DIR && echo "VITE_AUTH_API_ENDPOINT=${var.auth_lambda_url}" >> .env.production )

      (cd $ROOT_DIR && pnpm --filter @auth/frontend install && pnpm --filter @auth/frontend run build)

      aws s3 sync "$FRONTEND_DIR/dist" s3://${aws_s3_bucket.auth_website_bucket.bucket} --delete --exact-timestamps

    EOT
  }

}

这是我的node.js lambda处理程序,现在我没有进行重定向,而是尝试将成功登录后的请求转发到jwt.io

import {
  Handler,
  Context,
  APIGatewayProxyEventV2,
  APIGatewayProxyResultV2,
} from "aws-lambda";

import dotenv from "dotenv";
import { getAccessTokenFromUserCredentials } from "./utils/keycloak.js";
dotenv.config();

export const handler: Handler = async (
  event: APIGatewayProxyEventV2,
  context: Context
): Promise<APIGatewayProxyResultV2 | void> => {
  try {
    const { username, password } = JSON.parse(event.body ?? "");

    console.log(username, password);
    const tokens = await getAccessTokenFromUserCredentials(username, password);

    // const response = {
    //   statusCode: 200,
    //   body: JSON.stringify({ tokens }),
    //   headers: {
    //     "Access-Control-Allow-Origin": "*",
    //     "Access-Control-Allow-Methods": "OPTIONS,POST,GET",
    //     "Access-Control-Allow-Headers": "Content-Type",
    //     "Access-Control-Allow-Credentials": true,
    //   },
    // };

    const response = {
      statusCode: 301,
      statusDescription: "Temporary Redirect",
      headers: {
        location: "https://jwt.io",
        "Access-Control-Allow-Origin": "*",
        "Access-Control-Allow-Methods": "OPTIONS,POST,GET",
        "Access-Control-Allow-Headers": "Content-Type",
        "Access-Control-Allow-Credentials": true,
      },
      cookies: [],
    };

    return response;

  } catch (err: any) {
    console.log(err);
    return {
      statusCode: 401,
      body: err.message,
      headers: {
        "Access-Control-Allow-Origin": "*",
        "Access-Control-Allow-Methods": "OPTIONS,POST,GET",
        "Access-Control-Allow-Headers": "Content-Type",
        "Access-Control-Allow-Credentials": true,
      },
    };
  }
};

我真的很感谢一些帮助,我只是想了解有关 aws 和 terraform 的更多信息。我不想使用边缘 lambda,但想使用普通 lambda。

node.js amazon-web-services terraform cors amazon-cloudfront
1个回答
0
投票

经过大量研究,我发现尝试从 AJAX 调用进行重定向是一个很大的问题,这就是导致问题的原因。我已恢复返回重定向网址,然后从前端重定向到该网址。

© www.soinside.com 2019 - 2024. All rights reserved.