2016-01-19 1 views
13

У меня есть API за шлейфом API AWS, который должен использовать заголовок авторизации для обработки. К сожалению, я не смог передать это на сервер для обработки.Как пропустить заголовок авторизации через шлюз API до конечной точки HTTP?

Я попытался создать заголовок запроса авторизации 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?


Edit - Вот фрагмент кода, я использую, чтобы попасть в 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 конечную точку на внутреннем интерфейсе.

+0

Просто для уточнения ваших целей, вы хотите заголовок Auhthorization или результат аутентификации AWS? Вы включили AWS_IAM auth для метода? –

+2

Я хотел бы заголовок авторизации, который содержит подпись, учетные данные и т. Д. Я мог бы также использовать некоторую информацию о вызывающем абоненте (например, идентификатор пользователя), если заголовок аутентификации не может быть передан. Да, AWS_IAM включен для ресурса. – Nick

+0

Какой метод аутентификации вы используете для доступа к API? Учетные данные пользователя IAM, временные учетные данные на основе STS, учетные данные Cognito? –

ответ

6

Как отмечено в комментариях, заголовок Авторизация включает в себя неполную информацию для вас, чтобы установить, кто является пользователем, поэтому я бы не стал рекомендовать этот маршрут. Кроме того, если включен AWS_IAM, заголовок авторизации будет использоваться шлюзом API.

Если AWS_IAM auth включен и подпись поставлена ​​правильно, параметры $ context.identity должны отражать учетные данные, используемые для подписи запроса.

Если вы используете функцию вызова вызова в консоли, вы видите заполненные поля контекста?

Обновление: Я не могу воспроизвести эту проблему. У меня есть 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":{} 
} 
+0

Спасибо за отзывы. Я собирался получить AccessKey из заголовка Authorization, перебирать через наших пользователей и попытаться найти тот, у которого есть соответствующий AccessKey. Тем не менее, похоже, что это не сработает, поскольку заголовок авторизации будет потребляться при включении AWS_IAM. Я попробовал предложение, сделанное с параметрами $ context.identity. Используя функцию вызова вызова, я вижу, что они регистрируются моим API. Однако, когда я врезался в API-шлюз программно, тот же API попадает, но поля context.identity не заполняются и не регистрируются. Почему только для тестовой функции? – Nick

+0

@ Ник, что определенно не должно происходить. Вы уверены, что подписываете запросы? Не могли бы вы опубликовать код, который вы используете для вызова API? –

+0

Я обновил вопрос с помощью фрагмента кода. Я уверен, что подписываю запросы. – Nick

4

В настоящее время заголовок авторизации может быть перенаправлен только для методов, которые не требуют аутентификации AWS. Процесс подписи SigV4 зависит от заголовка авторизации, и мы не раскрываем его для целей безопасности. Если у вас есть данные, которые вам нужно отправить (помимо сигнатуры SigV4), вам нужно будет отправить другой заголовок.

1

В AWS API Gateway тело запроса не поддерживается для методов GET.

+1

Да, API Gateway не поддерживает тело запроса для метода GET на стороне интеграции. –

1

В запросе интеграции конвертируйте GET в POST, указав POST как ваш метод HTTP. Затем продолжите с указанием Body Mapping шаблона, предложенным @BobKinney

Таким образом, тело запроса будет распространяться правильно, но клиент все равно будет делать запрос GET, как ожидается