首页 > 解决方案 > 如何在作为 AWS Lambda 代理的一部分的 Micronaut API 实施中获取“requestContext”数据

问题描述

在 AWS Lambda 代理(作为使用 Cognito 授权的 API 网关中的集成)中,我想在处理请求时获取用户 ID。Lambda 是使用 Micronaut 用 Ja​​va 编写的。同一个 Lambda 被用作多个 API 端点的集成。

我发现 Cognito 用户 ID 包含在提供给 Lambda 处理程序的代理数据的 requestContext 条目中:

public class Handler implements RequestStreamHandler {
    private static MicronautLambdaContainerHandler handler = new MicronautLambdaContainerHandler();

    @Override
    public void handleRequest(InputStream input, OutputStream output, Context context) throws IOException {
        // `input` contains the information I need (see below)
        handler.proxyStream(input, output, context);
    }
}

当通过 API 调用 Lambda 并作为 Cognito 用户进行身份验证时,inputSteam 看起来像这样(省略了一些细节/更改为示例值,并且 Cognito 用户 ID 被标记为// !!!):

{
    "resource": "/test",
    "path": "/test",
    "httpMethod": "GET",
    "headers": {
        "accept": "application/json, text/plain, */*",
        "accept-encoding": "gzip, deflate, br",
        "accept-language": "en-US,en;q=0.5",
        "Authorization": "Bearer eyJraWQiOiJPWlgzYVg3UWNITFwvM09vODg4SzhaYjBlcmRJMjZNNWFRdXF3a3VyZWhaVT0iLCJhbGciOiJSUzI1NiJ9[...]",
        "cache-control": "no-cache",
        "Host": "api.my-app.example.com",
        "origin": "https://my-app.example.com",
        "pragma": "no-cache",
        "referer": "https://my-app.example.com/home",
        "User-Agent": "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:75.0) Gecko/20100101 Firefox/75.0",
        "X-Amzn-Trace-Id": "Root=1-5ebbd0f0-[...]",
        "X-Forwarded-For": "[...]",
        "X-Forwarded-Port": "443",
        "X-Forwarded-Proto": "https"
    },
    "multiValueHeaders": {
        /* similar to above but values being arrays */
    },
    "queryStringParameters": null,
    "multiValueQueryStringParameters": null,
    "pathParameters": {},
    "stageVariables": null,
    "requestContext": {
        "resourceId": "927cr8",
        "authorizer": {
            "claims": {
                "sub": "c99202cc-e088-43d6-8c15-1fd73a717a7c",    // !!!
                "cognito:groups": "[...]",
                "iss": "https://cognito-idp.eu-central-1.amazonaws.com/eu-central-1_[...]",
                "cognito:username": "c99202cc-e088-43d6-8c15-1fd73a717a7c",    // !!!
                "aud": "[...]",
                "event_id": "0d509360-d81e-4e7e-b346-9d018ed1cd04",
                "token_use": "id",
                "custom:[...]": "[...]",
                "auth_time": "1587536524",
                "name": "[...]",
                "exp": "Wed May 13 11:45:53 UTC 2020",
                "iat": "Wed May 13 10:45:53 UTC 2020",
                "email": "[...]"
            }
        },
        "resourcePath": "/test",
        "httpMethod": "GET",
        "extendedRequestId": "Md2VmF0OFiAFhZA=",
        "requestTime": "13/May/2020:10:50:24 +0000",
        "path": "/test",
        "accountId": "[...]",
        "protocol": "HTTP/1.1",
        "stage": "default",
        "domainPrefix": "api",
        "requestTimeEpoch": 1589367024516,
        "requestId": "feb2c8b2-4cf6-405b-bc48-76ebe33fde62",
        "identity": {
            "cognitoIdentityPoolId": null,
            "accountId": null,
            "cognitoIdentityId": null,
            "caller": null,
            "sourceIp": "[...]",
            "principalOrgId": null,
            "accessKey": null,
            "cognitoAuthenticationType": null,
            "cognitoAuthenticationProvider": null,
            "userArn": null,
            "userAgent": "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:75.0) Gecko/20100101 Firefox/75.0",
            "user": null
        },
        "domainName": "api.my-app.example.com",
        "apiId": "[...]"
    },
    "body": null,
    "isBase64Encoded": false
}

然后,MicronautLambdaContainerHandler 在幕后做了很多我还不完全理解的事情;但是,最后我可以在控制器类中使用 micronaut 的注释来定义 API 端点:

@Controller("/")
public class TestController {

    @Get("/test")
    public HttpResponse<String> test(HttpRequest<?> request) {
        String userId = ?
    }

}

这个例子是一个请求处理程序GET /test

HttpRequest 对象包含原始请求中的所有内容,例如标头和内容,但不包含 AWS 网关添加到其中的信息,例如身份验证的结果。

我现在如何访问它,特别是 requestContext,它是通过其输入传递给 Lambda 的?我在这里遗漏了一些难题。

标签: amazon-web-servicesaws-lambdaaws-api-gatewaymicronautmicronaut-aws

解决方案


Micronaut 定义了两种类型的绑定器,称为AwsProxyRequestArgumentBinderContextArgumentBinder。根据文档类型,绑定参数只能在方法参数中按类型要求。

所以你应该能够使用其中之一:

import com.amazonaws.services.lambda.runtime.Context;
import com.amazonaws.serverless.proxy.model.AwsProxyRequest;

@Controller("/")
public class TestController {

    @Get("/test")
    public HttpResponse<String> test(HttpRequest<?> request, Context context) {
        String userId = context.getAuthorizer().getClaims().getSubject();
    }

    @Get("/test")
    public HttpResponse<String> test2(HttpRequest<?> request, AwsProxyRequest awsRequest) {
        String userId = awsRequest.getRequestContext().getAuthorizer().getClaims().getSubject();
    }

}

如果这不起作用,也许只是从他们的源代码中复制这两个活页夹所做的事情。

source.getAttribute(RequestReader.LAMBDA_CONTEXT_PROPERTY);

或者:

((MicronautAwsProxyRequest<?>) source).getAwsProxyRequest();

推荐阅读