Kong API Gateway การใช้งาน (ตอนที่ 2)
Table of contents
Intro
สวัสดีครับ พบกับตอนที่ 2 จากคราวก่อนตอนที่ 1 จะเป็นการแนะนำและการติดตั้งโดยบทความนี้จะพามาลองใช้งาน Kong API Gateway โดยจะอธิบายเพื่อให้เห็นภาพและประโยชน์ในแง่การใช้งาน ซึ่งบทความมี 2 ตอนดังนี้
ทำความเข้าใจเบื้องต้น
ก่อนเริ่มใช้งาน Kong มาทำความเข้าใจหลักการเบื้องต้นก่อน โดยทั่วไป Kong API Gateway จะมีส่วนที่สำคัญ 3 ส่วนท ี่ผู้ใช้งานมือใหม่ควรทำความเข้าใจคือ
- Services คือการกำหนดชื่อของ Resources หรือชื่อของ Service ของ APIs เช่น Service Employees, Service Blog หรือ Service Billing
- Routes คือส่วนของ Path ของแต่ละ Endpoint (เป็น Subset ของ Services)
- Consumers คือผู้ที่สามารถใช้งาน API ได้ (เป็นแบบ Authenticated endpoints หมายถึงต้องมีการส่งข้อมูลอะไรบางอย่างเช่น API Keys หรือ JWT ยืนยันตัวตนก่อนใช้งานมากับ HTTP Headers)
ตัวอย่าง Use Cases
ตัวอย่างนี้จะใช้ข้อมูล JSON Placeholder API ของ https://jsonplaceholder.typicode.com ซึ่งเป็น Free fake data โดยจะใช้ Kong สร้าง Service 2 แบบดังนี้
-
Authenticated endpoints เข้าถึง API ได้โดยต้องแนบ API Keys หรือ JWT มากับ HTTP Headers
รูปภาพ อธิบายการทำงานของ Use Case (แบบ Authenticated endpoints)
-
Public endpoints เข้าถึง API ได้โดย
ไม่
ต้องแนบ API Keys หรือ JWT มากับ HTTP Headersรูปภาพ อธิบายการทำงานของ Use Case (แบบ Public endpoints)
กำหนด Services
เมื่อทำการ ติดตั้ง Kong และ Konga (ตอนที่ 1) เรียบร้อยแล้วให้ไปที่ URL ของ Konga ดังนี้
ที่เมนู: ไปที่ http://localhost:1337 -> Services -> Add New Service
ในหน้า Create Service ให้ระบุข้อมูลดังนี้
เมนู: Services -> Add New Service
กำหนดค่าดังนี้
Name: JSON-Placeholder
Description: API Resource of json placeholder
Protocol: https
Host: jsonplaceholder.typicode.com
Port: 443
Path: /
Retries: 5
Connect timeout: 60000
Write timeout: 60000
Read timeout: 60000
ถ้าท่านกำหนดช่อง Url ดังนั้นไม่จำเป็นต้องกำหนด Protocol Host และ Port
สร้าง Routes
กำหนดข้อมูล Routes ของแต่ละ Path
ที่เมนู: Service -> `JSON-Placeholder` (ที่ได้สร้างไว้แล้ว)
-> Routes -> `Add Route`
Users Route
/users
Name: Users
Path: /users (หลังระบุแล้วให้กด Enter ที่ช่อง Path)
Path handling: v1
Https redirect status code: 426
Regex priority: 0
Strip Path: No
Protocols: http, https
เพิ่ม Plugin JWT (/users)
เพิ่ม Plugin JWT สำหรับ Authenticated endpoints เข้าถึง API ได้โดยต้องแนบ JWT มากับ HTTP Headers
ที่เมนู: `JSON-Placeholder` -> Users (Route)
-> Plugins -> Add Plugin
เลือก Authentication: JWT (Add plugin)
กำหนดค่าดังนี้
key claim name: iss
secret is base64: No
run on preflight: Yes
maximum expiration: 0
Todos Route
/todos
Name: Todos
Path: /todos (หลังระบุแล้วให้กด Enter ที่ช่อง Path)
Path handling: v1
Https redirect status code: 426
Regex priority: 0
Strip Path: No
Protocols: http, https
เพิ่ม Plugin Key Auth (/todos)
เพิ่ม Plugin Key Auth สำหรับ Authenticated endpoints เข้าถึง API ไ ด้โดยต้องแนบ API Keys มากับ HTTP Headers
ที่เมนู: `JSON-Placeholder` -> Todos (Route)
-> Plugins -> Add Plugin
เลือก Authentication: Key Auth (Add plugin)
กำหนดค่าดังนี้
key names: x-api-key
hide credential: No
key in header: Yes
key in query: No
key in header: No
run on prefligh: Yes
Photos Route
/photos
Name: Photos
Path: /photos (หลังระบุแล้วให้กด Enter ที่ช่อง Path)
Path handling: v1
Https redirect status code: 426
Regex priority: 0
Strip Path: No
Protocols: http, https
*ไม่มีการกำหนด Authentication Plugin เนื่องจากเป็นแบบ Public endpoints
Posts Route
/posts
Name: Posts
Path: /posts (หลังระบุแล้วให้กด Enter ที่ช่อง Path)
Path handling: v1
Https redirect status code: 426
Regex priority: 0
Strip Path: No
Protocols: http, https
*ไม่มีการกำหนด Authentication Plugin เนื่องจากเป็นแบบ Public endpoints
กำหนด Consumers
ในส่วนนี้จะเป็นการเพิ่ม Consumer User สำหรับ Authentication ของ /users และ /todos
ที่เมนู: Consumer -> Create Consumer
username: kongvut (กำหนดชื่อที่ต้องการ)
Authenticated endpoints (API Keys)
ที่เมนู: Consumer -> <ชื่อ Consumer ที่สร้างไว้> -> Credentials (1)
-> API Keys (2) -> Create API Key (3)
กำหนดค่าดังนี้
key: dk627EXCU1lxfRLUVO9W2eJJ090Cxrig (กำหนดค่าที่ต้องการ)
Authenticated endpoints (JWT)
ที่เมนู: Consumer -> <ชื่อ Consumer ที่สร้างไว้> -> Credentials (1)
-> JWT (2) -> Create JWT (3)
กำหนดค่าดังนี้
secret: R60LCNzBIRfKpGj43V2usCvhdcS4F2su (กำหนดค่าที่ต้องการ)
{
"key": "W5gjacrhg7zY5FAyE8fq6OlQmDG7641w",
"id": "7be9e6ac-fbc2-4018-b923-814107a4481c",
"algorithm": "HS256",
"created_at": 1633793897,
"secret": "R60LCNzBIRfKpGj43V2usCvhdcS4F2su",
"tags": null,
"rsa_public_key": null,
"consumer": {
"id": "34dbbe8f-4407-41c6-ba84-1536f0a05172"
}
}
เมื่อได้ผลลัพธ์จากข้างบนเราสามารถใช้ Libraries ที่ช่วย Generate JWT ตามรายการดังนี้ https://jwt.io/libraries (แตกต่างกันไปตามภาษา)
ทดสอบ Generate JWT (Manual)
ตัวอย่างนี้จะทดลองใช้ JWT Builder แบบ Manual ของ http://jwtbuilder.jamiekurtz.com โดยกำหนดค่าดังนี้
Issuer: W5gjacrhg7zY5FAyE8fq6OlQmDG7641w (key)
Issued At: วันเวลาที่ออก Token
Expiration: วันเวลาที่ Token หมดอายุ
Audience: blog.2my.xyz (ชื่อผู้ที่รับรอง Token)
Subject: kongvut (id หรือ username ของผู้ใช้ Token)
Additional Claims (เพิ่มได้ตามที่ต้องการ)
FirstName: Kongvut
Surname: Sangkla
Email: [email protected]
Signed JSON Web Token
Key: R60LCNzBIRfKpGj43V2usCvhdcS4F2su (secret)
{
"iss": "W5gjacrhg7zY5FAyE8fq6OlQmDG7641w",
"iat": 1633791293,
"exp": 1665327293,
"aud": "blog.2my.xyz",
"sub": "kongvut",
"FirstName": "Kongvut",
"Surname": "Sangkla",
"Email": "[email protected]"
}
//ผลลัพธ์เมื่อกด Generate
//eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJXNWdqYWNyaGc3elk1RkF5RThmcTZPbFFtREc3NjQxdyIsImlhdCI6MTYzMzc5MTI5MywiZXhwIjoxNjY1MzI3MjkzLCJhdWQiOiJibG9nLjJteS54eXoiLCJzdWIiOiJrb25ndnV0IiwiRmlyc3ROYW1lIjoiS29uZ3Z1dCIsIlN1cm5hbWUiOiJTYW5na2xhIiwiRW1haWwiOiJrb25ndnV0QG1zbi5jb20ifQ.oqc58iIBhuJHkI4PtBVD5oO28ZJ47B6uNhUnDoQiOz4
ทดสอบการใช้งาน
ทดสอบการใช้งานจะแบ่งออกเป็น 2 ประเภทดังต่อไปนี้
แบบ Public
ทดสอบเรียก API แบบ GET
สำหรับ /photos
และ /posts
ใช้งานทันที (เนื่องจากไม่ต้องส่ง key สำหรับ Authentication) โ ดยใช้ Postman
แบบ Authentication
ทดสอบเรียก API แบบ GET
สำหรับ /todos
และ /users
(โดยไม่ส่ง key ไปด้วย) จะพบปัญหาไม่สามารถเข้าถึง API ดังนี้
{
"message": "Unauthorized"
}
{
"message": "No API key found in request"
}
ทดสอบส่ง key ไปด้วย
curl --location --request GET 'http://localhost:8000/users' \
--header 'Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJXNWdqYWNyaGc3elk1RkF5RThmcTZPbFFtREc3NjQxdyIsImlhdCI6MTYzMzc5MTI5MywiZXhwIjoxNjY1MzI3MjkzLCJhdWQiOiJibG9nLjJteS54eXoiLCJzdWIiOiJrb25ndnV0IiwiR2l2ZW5OYW1lIjoiS29uZ3Z1dCIsIlN1cm5hbWUiOiJTYW5na2xhIiwiRW1haWwiOiJrb25ndnV0QG1zbi5jb20ifQ.tHfNdCiYVqFu3XJ1XwZepCGRXgKRwWPk8Ey8GoUDlgM'
curl --location --request GET 'http://localhost:8000/todos' \
--header 'x-api-key: dk627EXCU1lxfRLUVO9W2eJJ090Cxrig'
ถ้าไม่มีอะไรผิดพลาดคุณจะได้รับผลลัพธ์ข้อมูล Resource ของ /users และ /todos ในรูปแบบ JSON
สรุป
ก็คงพอมองเห็นภาพรวมการใช้งานเบื้องต้น แต่ในการใช้งานจริง ๆ นั้นควรมีการกำหนด Namespace และ API Version ของ API เพื่อให้รองรับการทำงานที่หลากหลาย หรือหากมี Services เพิ่มเติมในอนาคตตัวอย่างเช่น
- http://localhost:8000/api/staff/v1/users
- http://localhost:8000/api/staff/v1/todos
- http://localhost:8000/api/staff/v2/posts
- http://localhost:8000/api/staff/v2/photos
- http://localhost:8000/api/shop/v1/items
- http://localhost:8000/api/shop/v1/categories
- http://localhost:8000/api/shop/v2/promotions
ทั้งนี้รายละเอียดของ Kong ยังมีอยู่อีกเยอะมากแนะนำอ่าน Documents เพิ่มเติมที่ References เนื่องจากจะมีรายละเอียดการทำ Upstream Object สำหรับการทำ Load balance เพื่อให้รองรับ Requests ที่มากขึ้นและการทำ Fail Over เมื่อบาง Service ไม่สามารถเข้าถึงได้จะมี Service อื่นมาทดแทน อีกทั้งยังมี หัวข้ออื่น ๆ ที่น่าสนใจที่ควรอ่านเพิ่มเช่น
- Path handling: v0, v1
- Strip Path: การทำ strip เมื่อ matching กับ path
- Preserve Host: การกำหนด host ที่สามารถใช้ API ได้