วิธีทำ Kong ให้รับ JWT + วิธี Generate JWT Token ด้วย Quarkus (Smallrye JWT)
daftyw
Posted on October 12, 2020
กว่าจะทำได้กินเวลาไป 2 ชม.เต็มๆ
เรื่องมันเริ่มจากอยากใช้ Kong ในการ Authentication ผ่าน JWT ที่ Sign แบบ RS256 (RSA) เพื่อให้มีความปลอดภัยขั้นสูงนิดนึง (นิดเดียวจริงๆ) ทางฝั่ง Consumer ก็คือ Sign JWT จาก Private Key ไป ทางฝั่งเราก้อขอแค่ Public Key สำหรับ Decryption แค่นั้นพอ
แต่... แล้วพอทำจริงมันก็ช่างยากเย็นซะเหลือเกิน อาจจะเพราะเราใช้ Quarkus ในการ Generate JWT Token ด้วย ตานี้ RSA Private มันเลยแปลกๆ เลยติดอยู่ตรงนี้ซะนาน
เอาล่ะมาเริ่มกันเลยแล้วกัน Step มีดังนี้ !!
ขั้น 1 Generate public/private key
เริ่มที่ Private key ก่อน (ถ้าเค้าถามก็ใส่ Subject ไปให้ครบ ตัวนี้ล่ะสำคัญเลยล่ะ C,O,CN)
$ openssl req -x509 -newkey rsa:2048 -nodes -keyout ./private-key.pem -days 365
ต่อไปก็ Generate เอา Public key
$ openssl rsa -in private-key.pem -out pub-key.pem -outform PEM -pubout
ได้มาสองอันแระเป็น
- private-key.pem (ซึ่งเราจะเอาไปใส่ใน App เพื่อ Gen)
- pub-key.pem (เราจะเอาตัวนี้ไปเพิ่มใน Consumer Credential ของ Kong)
ขั้น 2 เอา public key ไปใส่ใน Kong
ก่อนอื่นต้องเท้าความว่าเราใช้แบบ unsecured admin port สำหรับ Kong ก็จะเป็นการยิง curl รัวๆ ตามนี้
- เพิ่ม Consumer
$ curl -s -X POST http://$HOST:8001/consumers -d 'username=rawin' -d 'custom_id=rawin' | jq
{
"custom_id": "rawin",
"created_at": 1602513121,
"id": "3c3693c2-db73-4740-8d96-4e46645c4874",
"tags": null,
"username": "rawin"
}
- เพิ่ม RS256 Public Key
$ curl -s -X POST http://$H:32001/consumers/rawin/jwt -F 'rsa_public_key=@pub-key.pem' | jq
{
"created_at": 1602513212,
"id": "6467fcdc-ac73-48a6-b2d6-c78a633b59f9",
"tags": null,
"secret": "sIjCL1CWZvCye0N9w8op8aHcQGRYxzdf",
"rsa_public_key": "-----BEGIN PUBLIC KEY-----\r\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnilPbARTGCm19Tq4xxeT\r\ngeQUfXzAmr7LMpGkcvFiAKf0Yo0hi9SX55B85d0l22EEk5mhwLXcA+orXB8dq9ke\r\nS0mjWjFedXPQfjFCRUyIyQYt75qaDX5RJX+p6YN/VJCZkjlSI1UeaX8rOOmcBuUG\r\nHinV+qMWIZmwvpFXt45HLIuCH2iLM+7/TiiIBHakNR/ogKTKQE7iHon9ZQ2eSakG\r\nUMOBcCrGG8itp2jXtk0oTlZgNtB5+i03HoyZqAZ6Ny7VX0EIlJFuukvuFbNzKZaI\r\nIs21/xnXlsqto7iP9rJViFLzswg1c41nkbpwC1oByvTUDH4pQ94+L1vdhSof6A7n\r\nDwIDAQAB\r\n-----END PUBLIC KEY-----\r\n",
"consumer": {
"id": "3c3693c2-db73-4740-8d96-4e46645c4874"
},
"key": "9SGoxUxJ8m8V7TaftQxtvBMMEwF3Q2mm",
"algorithm": "HS256"
}
- จากนั้นต้องมีแก้ไข algorithm ด้วยนิดหน่อยโดย
$ curl -s -d 'algorithm=RS256' -X PATCH http://$H:32001/consumers/rawin/jwt/6467fcdc-ac73-48a6-b2d6-c78a633b59f9 | jq
{
"created_at": 1602513212,
"id": "6467fcdc-ac73-48a6-b2d6-c78a633b59f9",
"tags": null,
"secret": "sIjCL1CWZvCye0N9w8op8aHcQGRYxzdf",
"rsa_public_key": "-----BEGIN PUBLIC KEY-----\r\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnilPbARTGCm19Tq4xxeT\r\ngeQU
fXzAmr7LMpGkcvFiAKf0Yo0hi9SX55B85d0l22EEk5mhwLXcA+orXB8dq9ke\r\nS0mjWjFedXPQfjFCRUyIyQYt75qaDX5RJX+p6YN/VJCZkjlSI1UeaX8rOOm
cBuUG\r\nHinV+qMWIZmwvpFXt45HLIuCH2iLM+7/TiiIBHakNR/ogKTKQE7iHon9ZQ2eSakG\r\nUMOBcCrGG8itp2jXtk0oTlZgNtB5+i03HoyZqAZ6Ny7VX0
EIlJFuukvuFbNzKZaI\r\nIs21/xnXlsqto7iP9rJViFLzswg1c41nkbpwC1oByvTUDH4pQ94+L1vdhSof6A7n\r\nDwIDAQAB\r\n-----END PUBLIC KEY--
---\r\n",
"consumer": {
"id": "3c3693c2-db73-4740-8d96-4e46645c4874"
},
"key": "9SGoxUxJ8m8V7TaftQxtvBMMEwF3Q2mm",
"algorithm": "RS256"
}
เป็นอันเรียบร้อยที่ฝั่ง Kong (อันนี้คือเรามีตัว JWT Plugins ลงไว้เป็น Global Mode แล้วนะ)
ขั้นที่ 3 Generate JWT Token จาก Quarkus
ขั้นนี้มีซับซ้อนนิดหน่อยคือ เราต้องเอา field "key" มาใส่ในช่อง issuer ของ JWT token ด้วยโดยสมมุติว่าเรามี Quarkus Project อยู่แล้วก็เพิ่ม Extensions ไปก่อนโดย
$ ./mvnw quarkus:add-extensions -Dextension="quarkus-smallrye-jwt"
ต่อมาก็ Copy File private-key.pem ไปใส่ที่ src/main/resources
แล้วไปเพิ่มในไฟล์ application.properties หนึ่งบรรทัดตามด้านล่างนี้
smallrye.jwt.sign.key-location=private-key.pem
ถือว่าอ้างอิง Private key เสร็จสิ้นก็ทำ RestEasy Resource Class มาได้เลย
package org.rawin.jwt;
import javax.enterprise.context.RequestScoped;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import io.smallrye.jwt.build.Jwt;
@Path("/key")
@RequestScoped
public class KeyResource {
@GET
public String generateKey() throws Exception {
String jwtOut = Jwt.claims()
.issuer("9SGoxUxJ8m8V7TaftQxtvBMMEwF3Q2mm") // เอาค่าจาก key มา
.sign();
return jwtOut;
}
}
จบตามนี้ก็พร้อมรัน
ขั้นที่ 4 ทดสอบ
หา JWT Token ก่อน
$ ./mvnw quarkus:dev
แล้วเรียก
$ curl -s http://localhost:8080/key
eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJlS042NTA2UzhlaXp5T2RYZzNUbFpoUUdhZEYxdWJFOSIsImlhdCI6MTYwMjUxMjIwMSwiZXhwIjoxNjAyNTEyNTAxLCJqdGkiOiIzSVpkYjRua0lBelJadW5YQ0dqQWVRIn0.M3KojWn78l9sAbcEqsekiKd-ukJktboRRs6CkrPz6JNfq03pSBTygu_Xf678Z45NGfTJy0wg6ftZj9x1h7GN4VBZ06nqm0Vqcmdx-st_thFswEVOrUlTxzNULMElOejJCybpYd2lwxA_3vYu_L5sj0fyvLfetev90zV0TDK8S91Xl_dfr75fo7hF1jPcdN_wmV3woB3YKhN3xV9zAJpn7F5o1oL2YaRutevVJMqGhidLgb1S8j0H8jB2smr3DZ7v8KNkkMvuCej0oU9f39BixNBsbgm7VnaWC-xoJqBGAEnSIeza00datSyp-NSZN-CB7gLISmunR3S_PQ4iIVGJCg
เอาคีย์มาเรียกเข้าที่ Kong
เรียกแบบไม่ใส่ JWT Token
$ curl -s http://$HOST:8000/office/reports
{"message":"Unauthorized"}
เรียกแบบใส่ JWT Token
$ curl -s http://$HOST:8000/office/reports -H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJlS042NTA2UzhlaXp5T2RYZzNUbFpoUUdhZEYxdWJFOSIsImlhdCI6MTYwMjUxMjIwMSwiZXhwIjoxNjAyNTEyNTAxLCJqdGkiOiIzSVpkYjRua0lBelJadW5YQ0dqQWVRIn0.M3KojWn78l9sAbcEqsekiKd-ukJktboRRs6CkrPz6JNfq03pSBTygu_Xf678Z45NGfTJy0wg6ftZj9x1h7GN4VBZ06nqm0Vqcmdx-st_thFswEVOrUlTxzNULMElOejJCybpYd2lwxA_3vYu_L5sj0fyvLfetev90zV0TDK8S91Xl_dfr75fo7hF1jPcdN_wmV3woB3YKhN3xV9zAJpn7F5o1oL2YaRutevVJMqGhidLgb1S8j0H8jB2smr3DZ7v8KNkkMvuCej0oU9f39BixNBsbgm7VnaWC-xoJqBGAEnSIeza00datSyp-NSZN-CB7gLISmunR3S_PQ4iIVGJCg"
[]
ถือว่าประสบความสำเร็จ รีบมาเขียนไว้นี่คือเก็บไว้ดูเผื่อลืม
ใครสนใจหรือมีคำถามมาคุยกันที่ twitter: @rawinng ครับ
Posted on October 12, 2020
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.
Related
October 12, 2020