Routing Dinamis Berdasarkan Kredensial Pengguna dengan API Gateway

Bobur Umurzokov

Bobur Umurzokov

April 9, 2023

Technology

Dynamic Routing Berdasarkan Klaim JWT dengan Apache APISIX dan Okta

Dynamic routing adalah fitur yang sangat kuat dari sebagian besar API Gateway modern yang memungkinkan Anda untuk merutekan permintaan masuk secara real-time ke berbagai layanan backend berdasarkan berbagai kriteria seperti header HTTP, parameter query, atau bahkan body permintaan.

Dengan memanfaatkan plugin bawaan yang ada di Apache APISIX, pengembang juga dapat membuat aturan routing dinamis yang didasarkan pada berbagai kredensial pengguna seperti token akses, kunci API, atau ID pengguna. Dalam artikel ini, kita akan mengeksplorasi manfaat dari mengadopsi dynamic routing berdasarkan atribut autentikasi dengan Apache APISIX dan menunjukkan contoh konfigurasi cara merutekan permintaan klien secara dinamis ke layanan backend yang bertanggung jawab berdasarkan klaim token JWT.

Tujuan Pembelajaran

Anda akan mempelajari hal-hal berikut sepanjang artikel ini:

  • Merutekan lalu lintas secara dinamis dengan API Gateway.
  • Mengapa kita memerlukan Dynamic routing berdasarkan kredensial pengguna?
  • Dynamic routing berdasarkan klaim token JWT dengan Apache APISIX.

API Gateway: Merutekan Lalu Lintas Secara Dinamis

Merutekan lalu lintas secara dinamis dengan API Gateway dapat digunakan dalam berbagai aplikasi dan skenario untuk mengoptimalkan kinerja, meningkatkan keamanan, dan memastikan bahwa pengguna memiliki akses ke sumber daya yang sesuai.

Dengan merutekan lalu lintas secara dinamis, sebuah sistem dapat menyeimbangkan beban antara server yang berbeda atau layanan. Ini dapat membantu memastikan ketersediaan tinggi dengan merutekan lalu lintas ke layanan atau server yang tersedia. Jika satu layanan atau server gagal, lalu lintas dapat secara otomatis dialihkan ke layanan atau server lain yang tersedia.

merutekan lalu lintas secara dinamis dengan Apache APISIX

Dynamic routing juga dapat digunakan untuk merutekan lalu lintas berdasarkan geolokasi pengguna. Ini dapat membantu memastikan bahwa pengguna terhubung ke server atau layanan terdekat, meningkatkan waktu respons, dan mengurangi latensi.

merutekan lalu lintas berdasarkan geolokasi dengan Apache APISIX

API Gateway: Dynamic Routing Berbasis Identitas Pengguna

Seringkali, kita ingin merutekan lalu lintas ke layanan tertentu, atau jalur tertentu, atau hanya menampilkan data yang terkait dengan pengguna berdasarkan identitas yang diberikan oleh pengguna. Misalnya, dalam aplikasi multi-tenant, tenant yang berbeda mungkin memiliki akses ke layanan atau sumber daya yang berbeda. Dalam hal ini, API Gateway dapat merutekan lalu lintas hanya ke sumber daya tenant yang sesuai berdasarkan kredensial pengguna. Atau dalam aplikasi seluler, dapat merutekan lalu lintas ke layanan tertentu berdasarkan jenis perangkat atau sistem operasi.

Salah satu pendekatan umum adalah menggunakan token JWT untuk mengautentikasi dan mengotorisasi permintaan ke API. Ini berarti kita dapat membuat aturan routing yang kompleks dengan API Gateway yang mempertimbangkan klaim yang ada dalam token JWT dan menggunakan informasi ini untuk memutuskan ke mana permintaan akan diteruskan atau data apa yang akan ditampilkan. Pendekatan ini sangat berguna ketika Anda memiliki banyak pengguna dalam sistem yang memerlukan tingkat kontrol akses yang berbeda.

Merutekan lalu lintas secara dinamis berdasarkan token JWT dengan Apache APISIX

Demo: Dynamic Routing Berbasis Klaim Token JWT

Dalam demo ini, kita menggunakan API backend publik yang ada bernama Conference API dengan informasi sesi konferensi, pembicara, dan topik. Pada kenyataannya, ini bisa menjadi layanan backend Anda. Mari kita asumsikan bahwa kita ingin memfilter dan mengambil hanya sesi yang dimiliki oleh pembicara tertentu yang masuk ke sistem menggunakan kredensialnya seperti token JWT. Misalnya, https://conferenceapi.azurewebsites.net/speaker/1/sessions

permintaan hanya menampilkan sesi dari pembicara dengan ID unik dan ID unik ini berasal dari klaim token JWT sebagai bagian dari payload-nya. Lihat struktur payload token yang didekode di bawah ini, ada bidang speakerId yang juga disertakan:

Token JWT dengan klaim kustom

Dalam skenario ini, kita mengirim permintaan ke Route yang sama di API Gateway dan itu menghitung URI dinamis dari header otorisasi dan meneruskan permintaan ke URI (Lihat diagram di bawah untuk memahami alurnya). Untuk melakukannya, kita akan mengimplementasikan dynamic routing di tingkat API Gateway Apache APISIX berdasarkan klaim token JWT melalui penggunaan plugin berikut:

  1. Plugin openid-connect yang berinteraksi dengan penyedia identitas (IdP) dan dapat mencegat permintaan yang tidak terautentikasi tepat waktu ke aplikasi backend. Sebagai penyedia identitas, kita menggunakan Okta yang mengeluarkan token JWT dengan klaim kustom kami dan memvalidasi token JWT. Atau Anda dapat menggunakan IdP lain seperti Keycloak, dan Ory Hydra, atau Anda bahkan dapat menggunakan jwt-plugin untuk membuat token JWT, dan mengautentikasi serta mengotorisasi permintaan.
  2. Plugin serverless-pre-function untuk menulis kode fungsi Lua kustom yang mencegat permintaan, mendekode, mengurai klaim token JWT dan menyimpan nilai klaim dalam header kustom baru untuk membuat keputusan otorisasi lebih lanjut.
  3. Plugin proxy-rewrite, setelah kita memiliki klaim di header, kita menggunakan plugin ini sebagai mekanisme penerusan permintaan untuk menentukan jalur URI mana yang perlu digunakan untuk mengambil sesi khusus pembicara berdasarkan variabel header Nginx dalam kasus kami adalah speakerId yang secara dinamis berubah untuk membuat jalur yang berbeda /speaker/$http_speakerId/sessions. Plugin akan meneruskan permintaan ke sumber daya terkait di Conference API.

Setelah kita memahami apa yang akan kita bahas sepanjang demo, mari kita periksa prasyarat untuk memulai dengan mengonfigurasi skenario di atas dan menyelesaikan tutorial.

Prasyarat

  • Docker digunakan untuk menginstal etcd dan APISIX yang dikontainerisasi.
  • curl digunakan untuk mengirim permintaan ke APISIX untuk mengonfigurasi rute, upstream, dan konfigurasi plugin. Anda juga dapat menggunakan alat mudah seperti Postman untuk berinteraksi dengan API.
  • Apache APISIX diinstal di lingkungan target Anda. APISIX dapat dengan mudah diinstal dan dimulai dengan panduan cepat berikut.
  • Pastikan akun OKTA Anda telah dibuat, Anda mendaftarkan aplikasi baru (Anda dapat mengikuti panduan ini Mengonfigurasi Okta), menambahkan klaim kustom ke token menggunakan dasbor Okta, dan meminta token yang berisi klaim kustom yang disebut speakerId.

Mengonfigurasi Layanan Backend (upstream)

Anda perlu mengonfigurasi layanan backend untuk Conference API yang ingin Anda rutekan permintaannya. Ini dapat dilakukan dengan menambahkan server upstream di Apache APISIX melalui Admin API.

curl "http://127.0.0.1:9180/apisix/admin/upstreams/1" -X PUT -d ' { "name": "Conferences API upstream", "desc": "Register Conferences API as the upstream", "type": "roundrobin", "scheme": "https", "nodes": { "conferenceapi.azurewebsites.net:443": 1 } }'

Membuat Konfigurasi Plugin

Selanjutnya, kita menyiapkan objek konfigurasi plugin baru. Kami akan menggunakan 3 plugin openid-connect, serverless-pre-function dan proxy-rewrite masing-masing seperti yang telah kita bahas kasus penggunaan setiap plugin sebelumnya. Anda perlu mengganti hanya atribut plugin openid-connect (ClienID, Secret, Discovery dan Introspection endpoints) dengan detail Okta Anda sendiri sebelum Anda menjalankan perintah curl.

curl "http://127.0.0.1:9180/apisix/admin/plugin_configs/1" -X PUT -d ' { "plugins": { "openid-connect":{ "client_id":"{YOUR_OKTA_CLIENT_ID}", "client_secret":"{YOUR_OKTA_CLIENT_SECRET}", "discovery":"https://{YOUR_OKTA_ISSUER}/oauth2/default/.well-known/openid-configuration", "scope":"openid", "bearer_only":true, "realm":"master", "introspection_endpoint_auth_method":"https://{YOUR_OKTA_ISSUER}/oauth2/v1/introspect", "redirect_uri":"https://conferenceapi.azurewebsites.net/" }, "proxy-rewrite": { "uri": "/speaker/$http_speakerId/sessions", "host":"conferenceapi.azurewebsites.net" }, "serverless-pre-function": { "phase": "rewrite", "functions" : ["return function(conf, ctx) -- Import neccessary libraries local core = require(\"apisix.core\") local jwt = require(\"resty.jwt\") -- Retrieve the JWT token from the Authorization header local jwt_token = core.request.header(ctx, \"Authorization\") if jwt_token ~= nil then -- Remove the Bearer prefix from the JWT token local _, _, jwt_token_only = string.find(jwt_token, \"Bearer%s+(.+)\") if jwt_token_only ~= nil then -- Decode the JWT token local jwt_obj = jwt:load_jwt(jwt_token_only) if jwt_obj.valid then -- Retrieve the value of the speakerId claim from the JWT token local speakerId_claim_value = jwt_obj.payload.speakerId -- Store the speakerId claim value in the header variable core.request.set_header(ctx, \"speakerId\", speakerId_claim_value) end end end end "]} } }'

Dalam konfigurasi di atas, bagian yang paling sulit untuk dipahami mungkin adalah kode fungsi kustom yang kami tulis dalam Lua di dalam plugin serverless-pre-function:

return function(conf, ctx) -- Import neccessary libraries local core = require(\"apisix.core\") local jwt = require(\"resty.jwt\") -- Retrieve the JWT token from the Authorization header local jwt_token = core.request.header(ctx, \"Authorization\") if jwt_token ~= nil then -- Remove the Bearer prefix from the JWT token local _, _, jwt_token_only = string.find(jwt_token, \"Bearer%s+(.+)\") if jwt_token_only ~= nil then -- Decode the JWT token local jwt_obj = jwt:load_jwt(jwt_token_only) if jwt_obj.valid then -- Retrieve the value of the speakerId claim from the JWT token local speakerId_claim_value = jwt_obj.payload.speakerId -- Store the speakerId claim value in the header variable core.request.set_header(ctx, \"speakerId\", speakerId_claim_value) end end end end

Pada dasarnya, plugin ini akan dieksekusi sebelum dua plugin lainnya dan melakukan hal berikut:

  1. Mengambil token JWT dari header Authorization.
  2. Menghapus prefiks "Bearer " dari token JWT.
  3. Mendekode token JWT menggunakan library resty.jwt.
  4. Mengambil nilai klaim "speakerId" dari token JWT yang didekode.
  5. Akhirnya, menyimpan nilai klaim "speakerId" dalam variabel header speakerId.

Mengonfigurasi Rute Baru

Langkah ini melibatkan penyiapan rute baru yang menggunakan konfigurasi plugin, dan mengonfigurasi rute untuk bekerja dengan upstream (dengan mereferensikan ID mereka) yang kita buat di langkah sebelumnya:

curl "http://127.0.0.1:9180/apisix/admin/routes/1" -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d ' { "name":"Conferences API speaker sessions route", "desc":"Create a new route in APISIX for the Conferences API speaker sessions", "methods": ["GET"], "uri": "/sessions", "upstream_id":"1", "plugin_config_id":1 }'

Dalam konfigurasi di atas, kami mendefinisikan aturan pencocokan rute seperti hanya permintaan HTTP GET ke URI /sessions yang akan dirutekan ke layanan backend yang benar.

Memperoleh Token dari Okta

Setelah mengonfigurasi upstream, plugin, dan rute di sisi APISIX, sekarang kita meminta token dari Okta yang berisi klaim kustom speakerId kami. Anda dapat mengikuti panduan yang mencakup informasi tentang membangun URL untuk meminta token dengan Okta atau cukup menggunakan URL yang dihasilkan di bawah ini dengan penerbit Okta dan ID klien Anda:

https://{YOUR_OKTA_ISSUER}/oauth2/default/v1/authorize?client_id={YOUR_OKTA_CLIENT_ID} &response_type=id_token &scope=openid &redirect_uri=https%3A%2F%2Fconferenceapi.azurewebsites.net &state=myState &nonce=myNonceValue

Setelah Anda menempelkan permintaan ke browser Anda, browser akan dialihkan ke halaman masuk untuk Okta Anda dan menghasilkan ID Token.

https://conferenceapi.azurewebsites.net/#id_token={TOKEN_WILL_BE_HERE}

Perhatikan bahwa proses untuk mengambil token dapat berbeda dari penyedia identitas lainnya.

Untuk memeriksa ID token yang dikembalikan, Anda dapat menyalin nilainya dan menempelkannya ke decoder JWT apa pun (misalnya, https://token.dev).

Menguji Dynamic Routing

Akhirnya, sekarang kita dapat memverifikasi bahwa permintaan dirutekan ke jalur URI yang benar (dengan sesi khusus pembicara) berdasarkan kriteria pencocokan dan klaim token JWT dengan menjalankan perintah curl sederhana lainnya:

curl -i -X "GET [http://127.0.0.1:9080/sessions](http://127.0.0.1:9080/sessions)" -H "Authorization: Bearer {YOUR_OKTA_JWT_TOKEN}"

Inilah hasilnya, seperti yang kita harapkan. Jika kita mengatur speakerId ke 1 dalam klaim JWT Okta, Apisix merutekan permintaan ke jalur URI yang relevan dan mengembalikan semua sesi pembicara ini dalam respons.

{ "collection": { "version": "1.0", "links": [], "items": [ { "href": "https://conferenceapi.azurewebsites.net/session/114", "data": [ { "name": "Title", "value": "\r\n\t\t\tIntroduction to Windows Azure Part I\r\n\t\t" }, { "name": "Timeslot", "value": "04 December 2013 13:40 - 14:40" }, { "name": "Speaker", "value": "Scott Guthrie" } ], "links": [ { "rel": "http://tavis.net/rels/speaker", "href": "https://conferenceapi.azurewebsites.net/speaker/1" }, { "rel": "http://tavis.net/rels/topics", "href": "https://conferenceapi.azurewebsites.net/session/114/topics" } ] }, { "href": "https://conferenceapi.azurewebsites.net/session/121", "data": [ { "name": "Title", "value": "\r\n\t\t\tIntroduction to Windows Azure Part II\r\n\t\t" }, { "name": "Timeslot", "value": "04 December 2013 15:00 - 16:00" }, { "name": "Speaker", "value": "Scott Guthrie" } ], } ] } }

Kesimpulan

  • Dengan API Gateway, Anda dapat merutekan lalu lintas ke berbagai layanan backend berdasarkan berbagai kriteria.
  • Dynamic routing dapat dicapai tergantung pada atribut pengguna yang ditentukan dalam header, query, atau body permintaan.
  • Anda dapat membuat aturan routing yang kompleks yang mempertimbangkan klaim yang ada dalam token JWT, dan memastikan bahwa hanya permintaan yang diotorisasi yang diizinkan untuk mengakses API Anda.
Tags: