Java Lambda で API Gateway の Lambda Authorizer を実装する

API Gateway(Rest API) の Lambda Authorizer では、Lambda のレスポンスに [指定の JSON を返す](https://docs.aws.amazon.com/ja_jp/apigateway/latest/developerguide/api-gateway-lambda-authorizer-output.html) 必要があります。

Lambda Authorizer って何？って方は [こちら](https://docs.aws.amazon.com/ja_jp/apigateway/latest/developerguide/apigateway-use-lambda-authorizer.html) を。

Node.js だと、こんな感じで、そのまま JSON を返せます。便利ですね。

```JavaScript
exports.handler = async (event) => {
  console.log(JSON.stringify(event, null, 4));
  return {
    "principalId": "1234",
    "policyDocument": {
      "Version": "2012-10-17",
      "Statement": [
        {
          "Action": "execute-api:Invoke",
          "Effect": "Allow",
          "Resource": event.methodArn
        }
      ]
    }
  };
};
```

Java の場合は、JSON では返せないですし、JSON を文字列として返してもうまくいきません。
方法としては 2 つあって、Map を使う方法と POJO クラスを使う方法です。

## Map を使う方法
```Java
import com.amazonaws.services.lambda.runtime.Context;
import com.amazonaws.services.lambda.runtime.RequestHandler;

import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

public class MapLambdaAuthorizer implements RequestHandler<Map<String, Object>, Map<String, Object>> {
    @Override
    public Map<String, Object> handleRequest(Map<String, Object> event, Context lambdaContext) {
        Map<String, Object> response = new HashMap<String, Object>();
        response.put("principalId", "1234");

        Map<String, Object> policyDocument = new HashMap<String, Object>();
        policyDocument.put("Version", "2012-10-17");

        Map<String, String> statement = new HashMap<>();
        statement.put("Action", "execute-api:Invoke");
        statement.put("Effect", "Allow");
        statement.put("Resource", (String) event.get("methodArn"));
        policyDocument.put("Statement", Arrays.asList(statement));

        response.put("policyDocument", policyDocument);

        Map<String, String> context = new HashMap<String, String>();
        context.put("now", new Date().toString());
        response.put("context", context);

        return response;
    }
}
```
https://github.com/kazfuku/apigateway-java-lambda-authorizer/blob/master/lambda/authorizer/src/main/java/com/kazfuku/aws/MapLambdaAuthorizer.java

## POJO を使う方法
```Java
import com.amazonaws.services.lambda.runtime.Context;
import com.amazonaws.services.lambda.runtime.RequestHandler;

import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

public class PojoLambdaAuthorizer implements RequestHandler<Map<String, Object>, AuthorizerResponse> {
    @Override
    public AuthorizerResponse handleRequest(Map<String, Object> event, Context lambdaContext) {
        AuthorizerResponse response = new AuthorizerResponse();
        response.setPrincipalId("1234");

        Map<String, Object> policyDocument = new HashMap<>();
        policyDocument.put("Version", "2012-10-17");

        Map<String, String> statement = new HashMap<>();
        statement.put("Action", "execute-api:Invoke");
        statement.put("Effect", "Allow");
        statement.put("Resource", (String) event.get("methodArn"));
        policyDocument.put("Statement", Arrays.asList(statement));

        response.setPolicyDocument(policyDocument);

        Map<String, String> context = new HashMap<>();
        context.put("now", new Date().toString());
        response.setContext(context);

        return response;
    }
}
```
https://github.com/kazfuku/apigateway-java-lambda-authorizer/blob/master/lambda/authorizer/src/main/java/com/kazfuku/aws/PojoLambdaAuthorizer.java

```Java
import java.util.Map;

public class AuthorizerResponse {
    private String principalId;

    public void setPrincipalId(String principalId) {
        this.principalId = principalId;
    }

    public String getPrincipalId() {
        return this.principalId;
    }

    private Map<String, Object> policyDocument;

    public void setPolicyDocument(Map<String, Object> policyDocument) {
        this.policyDocument = policyDocument;
    }

    public Map<String, Object> getPolicyDocument() {
        return this.policyDocument;
    }

    private Map<String, String> context;

    public Map<String, String> getContext() {
        return context;
    }

    public void setContext(Map<String, String> context) {
        this.context = context;
    }
}
```
https://github.com/kazfuku/apigateway-java-lambda-authorizer/blob/master/lambda/authorizer/src/main/java/com/kazfuku/aws/AuthorizerResponse.java

POJO にするなら、

```Java
policyDocument.put("Version", "2012-10-17");
statement.put("Action", "execute-api:Invoke");
```

とかも POJO にしないと気持ち悪いところですが、POJO にした場合に API Gateway に返される JSON のキー名が小文字始まり(version、action)になってしまい、うまく解釈してくれませんでした。

## まとめ
JSON がそんなに大きくないので、Map 方式の方が読みやすくていいと思います。

## Source Code
https://github.com/kazfuku/apigateway-java-lambda-authorizer