Как передать заголовок авторизации через API-шлюз на конечную точку HTTP?
У меня есть API за шлейфом AWS API, который должен использовать заголовок авторизации для обработки. Я, к сожалению, не смог передать это на сервер для обработки.
Я попытался создать заголовок запроса авторизации HTTP в моем запросе метода и затем создать соответствующий заголовок HTTP-авторизации в моем запросе интеграции (авторизация сопоставляется с методом .request.header.Авторизация в этом случае). Я регистрирую все заголовки, получаемые бэкэнд, и из журнала я могу видеть другие заголовки, которые я указал в запросе интеграции, но не авторизацию.
Я также попытался создать шаблон сопоставления с 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. Я также не вижу пользователя ARN. Я видел другие примеры и темы, в которых пользователи упомянули о доступе к полю авторизации объекта события, который передается в функцию Lambda, но я не использую функцию Lambda.
Я выполнил развертывание шлюза API в обоих сценариях.
Кто-нибудь знает, есть ли способ передать заголовок авторизации через шлюз API для моей конечной точки HTTP? Есть ли альтернативный способ доступа к имени пользователя или идентификатору вызывающего API API?
Изменить - здесь фрагмент кода, который я использую, чтобы попасть в шлюз 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 на сервере.
Ответы
Ответ 1
Как отмечено в комментариях, заголовок авторизации включает в себя неполную информацию для вас, чтобы определить, кто является пользователем, поэтому я бы не стал рекомендовать этот маршрут. Кроме того, если AWS_IAM auth включен, заголовок авторизации будет использоваться шлюзом API.
Если AWS_IAM auth включен и подпись поставлена правильно, параметры $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('$')
}
И лямбда-функция, которая просто отплевывает входной сигнал в качестве выхода. Когда я подписываю запрос и вызываю 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":{}
}
Ответ 2
В настоящее время заголовок авторизации может быть перенаправлен только для методов, не требующих аутентификации AWS. Процесс подписи SigV4 зависит от заголовка авторизации, и мы не раскрываем его для целей безопасности. Если у вас есть данные, которые вам нужно отправить (помимо сигнатуры SigV4), вам нужно будет отправить другой заголовок.
Ответ 3
В AWS API Gateway тело запроса не поддерживается для методов GET.
Ответ 4
В запросе интеграции конвертируйте GET в POST, указав POST как ваш метод HTTP. Затем приступайте к заданию шаблона сопоставления тела, предложенного @BobKinney
Таким образом тело запроса будет правильно распространяться, но клиент все равно будет делать запрос GET как ожидалось