首页 文章

如何通过API网关将Authorization标头传递给HTTP endpoints ?

提问于
浏览
15

我在AWS API Gateway后面有一个API,需要使用Authorization标头进行处理 . 遗憾的是,我无法将其传递给后端进行处理 .

我已经尝试在我的方法请求中创建授权HTTP请求标头,然后在我的集成请求中创建相应的授权HTTP标头(在这种情况下,授权从method.request.header.Authorization映射) . 我记录了后端接收的所有头文件,并且从日志中,我可以看到我在集成请求中列出但未授权的其他头文件 .

我还尝试使用Content-Type application/json 创建一个映射模板,并将模板定义为

{
      "AccountID": "$context.identity.accountId",
      "Caller": "$context.identity.caller",
      "User": "$context.identity.user",
      "Authorization": "$input.params().header.get('Authorization')",
      "UserARN": "$context.identity.userArn"
  }

然而,后端日志显示JSON主体中仍然没有Authorization标头或任何Authorization字段 . 我也看不到用户的ARN . 我已经看到其他示例和线程,其中用户提到访问传递给Lambda函数的事件对象上的Authorization字段,但我没有使用Lambda函数 .

我确保在两种情况下都部署API网关 .

有谁知道我是否可以通过API网关将Authorization标头传递给我的HTTP endpoints ?是否有其他方法可以访问API调用者的用户名或ID?


编辑 - 这是我用来访问API网关的代码片段:

String awsAccessKey = "myaccesskey";
String awsSecretKey = "mysecretkey";

URL endpointUrl;
try {
    endpointUrl = new URL("https://<host>/<path>/<to>/<resource>?startDate=20151201&endDate=20151231");
} catch(Exception e) {
    throw new RuntimeException("Unable to parse service endpoint: " + e.getMessage());
}

Date now = new Date();

SimpleDateFormat sdf1 = new SimpleDateFormat("yyyyMMdd'T'HHmmss'Z'");
sdf1.setTimeZone(new SimpleTimeZone(0, "UTC"));
String dateTS = sdf1.format(now);

String headerNames = "host;x-amz-date";
String queryParameters = "endDate=20151231&startDate=20151201";

String canonicalRequest = "GET\n" +
        "/<path>/<to>/<resource>\n" +
        queryParameters + "\n" +
        "host:<host>\n" +
        "x-amz-date:" + dateTS + "\n" +
        "\n" +
        headerNames + "\n" +
        "<sha256 hash for empty request body>";

System.out.println(canonicalRequest);

SimpleDateFormat sdf2 = new SimpleDateFormat("yyyyMMdd");
sdf2.setTimeZone(new SimpleTimeZone(0, "UTC"));
String dateStr = sdf2.format(now);
String scope =  dateStr + "/us-east-1/execute-api/aws4_request";
String stringToSign =
        "AWS4-HMAC-SHA256\n" +
               dateTS + "\n" +
               scope + "\n" +
               "hex encoded hash of canonicalRequest";

System.out.println(stringToSign);

byte[] kSecret = ("AWS4" + awsSecretKey).getBytes();
byte[] kDate = HmacSHA256(dateStr, kSecret);
byte[] kRegion = HmacSHA256("us-east-1", kDate);
byte[] kService = HmacSHA256("execute-api", kRegion);
byte[] kSigning = HmacSHA256("aws4_request", kService);
byte[] signature = HmacSHA256(stringToSign, kSigning);
String credentialsAuthorizationHeader = "Credential=" + awsAccessKey + "/" + scope;
String signedHeadersAuthorizationHeader = "SignedHeaders=" + headerNames;
String signatureAuthorizationHeader = "Signature=" + "hex encoded signature";
String authorization = "AWS4-HMAC-SHA256 "
        + credentialsAuthorizationHeader + ", "
        + signedHeadersAuthorizationHeader + ", "
        + signatureAuthorizationHeader;

Map<String, String> headers = new HashMap<String, String>();
headers.put("x-amz-date", dateTS);
headers.put("Host", endpointUrl.getHost());
headers.put("Authorization", authorization);
headers.put("Content-Type", "application/json");

HttpURLConnection connection = null;
try {
    connection = (HttpURLConnection) endpointUrl.openConnection();
    connection.setRequestMethod("GET");

    for (String headerKey : headers.keySet()) {
        connection.setRequestProperty(headerKey, headers.get(headerKey));
    }
    connection.setUseCaches(false);
    connection.setDoInput(true);
    connection.setDoOutput(true);

    InputStream is;
    try {
        is = connection.getInputStream();
    } catch (IOException e) {
        is = connection.getErrorStream();
    }

    BufferedReader rd = new BufferedReader(new InputStreamReader(is));
    String line;
    StringBuffer response = new StringBuffer();
    while ((line = rd.readLine()) != null) {
        response.append(line);
        response.append('\r');
    }
    rd.close();
    System.out.println(response.toString());
} catch (Exception e) {
    throw new RuntimeException("Error: " + e.getMessage(), e);
} finally {
    if (connection != null) {
        connection.disconnect();
    }
}

这足以成功进行身份验证并在后端命中HTTP endpoints .

4 回答

  • 4

    如评论中所述,授权 Headers 包含不完整的信息,您可以确定该用户是谁,因此我不建议使用此路由 . 此外,如果启用了AWS_IAM auth,则API Gateway将使用Authorization标头 .

    如果启用了AWS_IAM身份验证并且正确提供了签名,则$ context.identity参数应反映用于签署请求的凭据 .

    如果您在控制台中使用测试调用功能,您是否看到正在填写的上下文字段?

    Update: 我无法重现此问题 . 我有一个带有以下映射模板的API:

    #set($path = $input.params().path)
    #set($qs = $input.params().querystring)
    {
        "resource-path": "$context.resourcePath",
        "http-method": "$context.httpMethod",
        "identity": {
            #foreach($key in $context.identity.keySet())
                "$key": "$context.identity.get($key)"
            #if($foreach.hasNext), #end
            #end
        },
        "params": {
            #foreach($key in $path.keySet())
                "$key": "$path.get($key)"
            #if($foreach.hasNext), #end
            #end
        },
        "query": {
            #foreach($key in $qs.keySet())
                "$key": "$qs.get($key)"
            #if($foreach.hasNext), #end
            #end
        },
        "body": $input.json('$')
    }
    

    还有一个简单地将输入作为输出吐出的lambda函数 . 当我签署请求并调用API时,我得到了预期的结果:

    {
      "resource-path":"/iam",
      "http-method":"GET",
      "identity":{ 
        "cognitoIdentityPoolId":"",
        "accountId":"xxxxxxxx",
        "cognitoIdentityId":"",
        "caller":"AIDXXXXXXXXXXX,
        "apiKey":"",
        "sourceIp":"54.xx.xx.xx",
        "cognitoAuthenticationType":"",
        "cognitoAuthenticationProvider":"",
        "userArn":"arn:aws:iam::xxxxxxxx:user/hackathon",
        "userAgent":"Java/1.8.0_31",
        "user":"AIDXXXXXXXXXXXXXX"
      },
      "params":{},
      "query":{},
      "body":{}
    }
    
  • 1

    目前,只能为不需要AWS身份验证的方法转发Authorization标头 . SigV4签名过程依赖于Authorization标头,我们不会出于安全目的而公开此标识 . 如果您有需要发送的数据(除了SigV4签名),您还需要发送另一个标头 .

  • 7

    在AWS API Gateway中,GET方法不支持Request Body .

  • 1

    在Integration Request中,通过将POST指定为HTTP方法,将GET转换为POST . 然后按照@BobKinney的建议继续指定Body Mapping模板

    这样,请求正文将正确传播,但客户端仍将按预期发出GET请求

相关问题