2016-01-19 3 views
13

Ich habe eine API hinter einem AWS API Gateway, die den Berechtigungsheader für die Verarbeitung verwenden muss. Ich konnte dies leider nicht an das Backend zur Bearbeitung weitergeben.Wie übergeben Sie den Autorisierungsheader über das API-Gateway an den HTTP-Endpunkt?

Ich habe versucht, die Autorisierung HTTP Request Header in meiner Methode Anfrage und dann die Erstellung der entsprechenden Autorisierung HTTP-Header in meiner Integrationsanfrage (Autorisierung wird in diesem Fall von method.request.header.Authorization zugeordnet). Ich protokolliere alle Header, die das Backend empfängt, und aus dem Protokoll kann ich andere Header sehen, die ich in der Integrationsanforderung aber nicht Autorisierung aufgelistet habe.

ich auch versucht haben, eine Mapping-Vorlage mit Content-Type application/json und die Vorlage als

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

Doch definiert Erstellen der Backend-Protokolle zeigen, dass es immer noch keine Authorization-Header ist noch ein Berechtigungsfeld im JSON-Körper . Ich kann den ARN des Benutzers auch nicht sehen. Ich habe andere Beispiele und Threads gesehen, bei denen Benutzer erwähnt haben, dass sie auf das Berechtigungsfeld des Ereignisobjekts zugreifen, das an eine Lambda-Funktion übergeben wird, aber ich verwende keine Lambda-Funktion.

Ich habe sichergestellt, das API-Gateway in beiden Szenarien bereitzustellen.

Weiß jemand, ob es eine Möglichkeit gibt, den Authorization-Header über das API-Gateway an meinen HTTP-Endpunkt zu übergeben? Gibt es eine alternative Möglichkeit, auf den Benutzernamen oder die ID des API-Aufrufers zuzugreifen?


Bearbeiten - Hier ist ein Ausschnitt des Codes des API-Gateway Ich verwende zu treffen:

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(); 
    } 
} 

Das ist gut genug, um erfolgreich und treffen den HTTP-Endpunkt auf dem Back-End zu authentifizieren.

+0

Sie Ihre Ziele zu klären, wollen Sie der Ahthorization-Header oder das Ergebnis der AWS-Authentifizierung? Haben Sie die AWS_IAM-Authentifizierung für die Methode aktiviert? –

+2

Ich möchte die Authorization-Header, die Signatur, Anmeldeinformationen usw. enthält. Ich könnte auch einige Informationen über den Anrufer (z. B. Benutzer-ID) verwenden, wenn Authentication Header nicht durchgereicht werden kann. Ja, AWS_IAM ist für die Ressource aktiviert. – Nick

+0

Welche Authentifizierungsmethode verwenden Sie für den Zugriff auf die API? IAM-Benutzeranmeldeinformationen, temporäre STS-rollenbasierte Anmeldeinformationen, Cognito-Anmeldeinformationen? –

Antwort

6

Wie in den Kommentaren erwähnt, enthält die Autorisierungskopfzeile unvollständige Informationen für Sie, um festzustellen, wer der Benutzer ist, also würde ich nicht empfehlen, diese Route zu gehen. Wenn die AWS_IAM-Authentifizierung aktiviert ist, wird der Berechtigungsheader außerdem von API Gateway verwendet.

Wenn AWS_IAM auth aktiviert ist und die Signatur korrekt angegeben wird, sollten die Parameter $ context.identity die Anmeldeinformationen angeben, die zum Signieren der Anforderung verwendet wurden.

Wenn Sie die Testaufruffunktion in der Konsole verwenden, sehen Sie, dass die Kontextfelder ausgefüllt sind?

Aktualisierung: Ich kann dieses Problem nicht reproduzieren. Ich habe eine API mit der folgenden Mapping-Vorlage:

#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('$') 
} 

Und eine Lambda-Funktion, die einfach die Eingabe als Ausgabe spuckt zurück. Als ich den Antrag unterzeichnen und die API aufrufen, erhalte ich die erwarteten Ergebnisse zurück:

{ 
    "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

Danke für das Feedback bis jetzt. Ich wollte den AccessKey aus dem Autorisierungsheader holen, durch unsere Benutzer iterieren und versuchen, einen zu finden, der einen passenden AccessKey hat. Es klingt jedoch so, als würde das nicht funktionieren, da der Authorization-Header verbraucht wird, wenn AWS_IAM auth aktiviert ist. Ich habe den Vorschlag mit den Parametern $ context.identity versucht. Mit der Testaufruffunktion kann ich sehen, dass sie von meiner API protokolliert werden. Wenn ich jedoch das API-Gateway programmatisch betrete, wird die gleiche API getroffen, aber die field.identity-Felder werden nicht aufgefüllt und protokolliert. Warum nur für die Testfunktion? – Nick

+0

@Nick das sollte definitiv nicht passieren. Sind Sie sicher, dass Sie Anfragen signieren? Können Sie den Code posten, den Sie zum Aufrufen der API verwenden? –

+0

Ich habe die Frage mit einem Code-Snippet aktualisiert. Ich bin sicher, dass ich Anträge unterschreibe. – Nick

4

Derzeit kann der Authorization-Header nur für Methoden weitergeleitet werden, die AWS-Authentifizierung nicht benötigen. Der SigV4-Signaturprozess basiert auf dem Authorization-Header und wir stellen diesen aus Sicherheitsgründen nicht zur Verfügung. Wenn Sie Daten haben, die Sie senden müssen (neben der SigV4-Signatur), müssen Sie eine andere Kopfzeile senden.

1

In AWS API Gateway wird Request Body für GET-Methoden nicht unterstützt.

+1

Ja, API-Gateway unterstützt Anfragekörper für GET-Methode auf der Integrationsseite nicht. –

1

In Integrationsanforderung konvertieren Sie Ihre GET zu POST, indem Sie POST als Ihre HTTP-Methode angeben. Dann fahren Sie mit dem Body Mapping Template spezifiziert, wie durch @BobKinney vorgeschlagen

Auf diese Weise der Anfrage Körper richtig ausbreiten wird, sondern der Kunde wird immer noch eine GET-Anforderung werden machen, wie erwartet

Verwandte Themen