it-swarm.asia

Bagaimana Anda meneruskan tajuk Otorisasi melalui API Gateway ke HTTP endpoint?

Saya memiliki API di belakang AWS API Gateway yang perlu menggunakan header Otorisasi untuk diproses. Sayangnya saya tidak dapat meneruskan ini ke backend untuk diproses.

Saya telah mencoba membuat Header Permintaan HTTP Otorisasi dalam Permintaan Metode saya dan kemudian membuat Header HTTP Otorisasi yang sesuai dalam Permintaan Integrasi saya (Otorisasi dipetakan dari method.request.header. Kewenangan dalam hal ini). Saya mencatat semua header yang diterima backend, dan dari log, saya bisa melihat header lain yang telah saya daftarkan dalam Permintaan Integrasi tetapi bukan Otorisasi. 

Saya juga telah mencoba membuat templat pemetaan dengan Content-Type application/json dan templat didefinisikan sebagai

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

Namun, log backend menunjukkan bahwa masih belum ada header Otorisasi atau bidang Otorisasi apa pun di tubuh JSON. Saya juga tidak bisa melihat ARN pengguna. Saya telah melihat contoh dan utas lain di mana pengguna telah menyebutkan mengakses bidang Otorisasi pada objek acara yang dilewatkan ke fungsi Lambda, tetapi saya tidak menggunakan fungsi Lambda. 

Saya telah memastikan untuk Menyebarkan API Gateway di kedua skenario.

Adakah yang tahu kalau ada cara agar saya bisa meneruskan tajuk Otorisasi melalui API Gateway ke titik akhir HTTP saya? Apakah ada cara alternatif untuk mengakses nama pengguna atau ID pemanggil API?


Sunting - Ini cuplikan dari kode yang saya gunakan untuk menekan API Gateway:

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

Ini cukup baik untuk mengotentikasi berhasil dan tekan titik akhir HTTP di backend.

15
Nick

Seperti yang tercantum dalam komentar, tajuk Otorisasi menyertakan informasi yang tidak lengkap bagi Anda untuk menentukan siapa pengguna itu, jadi saya tidak akan merekomendasikan pergi rute ini. Selain itu, jika AWS_IAM auth diaktifkan, tajuk Otorisasi akan dikonsumsi oleh API Gateway.

Jika AWS_IAM auth diaktifkan dan tanda tangan diberikan dengan benar, parameter $ context.identity harus mencerminkan kredensial yang digunakan untuk menandatangani permintaan.

Jika Anda menggunakan fitur uji pemanggilan di konsol, apakah Anda melihat bidang konteks sedang diisi?

Perbarui: Saya tidak dapat mereproduksi masalah ini . Saya memiliki API dengan templat pemetaan berikut:

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

Dan fungsi lambda yang hanya memuntahkan kembali input sebagai output. Ketika saya menandatangani permintaan dan menjalankan API, saya mendapatkan kembali hasil yang diharapkan:

{
  "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":{}
}
7
Bob Kinney

Saat ini header Otorisasi hanya dapat diteruskan untuk metode yang tidak memerlukan otentikasi AWS. Proses penandatanganan SigV4 bergantung pada header Otorisasi dan kami tidak mengekspos ini untuk tujuan keamanan. Jika Anda memiliki data yang perlu Anda kirim (selain tanda tangan SigV4), Anda harus mengirim header lain.

4
RyanG

Di AWS API Gateway, Badan Permintaan tidak didukung untuk metode GET.

1
jlorie

Dalam Permintaan Integrasi, konversi GET Anda menjadi POST dengan menetapkan POST sebagai metode HTTP Anda. Kemudian lanjutkan dengan menentukan Templat Pemetaan Tubuh seperti yang diusulkan oleh @ BobKinney

Dengan cara ini badan permintaan akan menyebar dengan baik, tetapi klien masih akan membuat permintaan GET seperti yang diharapkan

1
malenkiy_scot