DEV Community

daftyw
daftyw

Posted on • Updated on

วิธีทำ Kong ให้รับ JWT + วิธี Generate JWT Token ด้วย Quarkus (Smallrye JWT)

กว่าจะทำได้กินเวลาไป 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
Enter fullscreen mode Exit fullscreen mode

ต่อไปก็ Generate เอา Public key

$ openssl rsa -in private-key.pem -out pub-key.pem -outform PEM -pubout
Enter fullscreen mode Exit fullscreen mode

ได้มาสองอันแระเป็น

  • 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"                            
}                                                
Enter fullscreen mode Exit fullscreen mode
  • เพิ่ม 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"
}
Enter fullscreen mode Exit fullscreen mode
  • จากนั้นต้องมีแก้ไข 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"                                                                                                     
}
Enter fullscreen mode Exit fullscreen mode

เป็นอันเรียบร้อยที่ฝั่ง 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"
Enter fullscreen mode Exit fullscreen mode

ต่อมาก็ Copy File private-key.pem ไปใส่ที่ src/main/resources

แล้วไปเพิ่มในไฟล์ application.properties หนึ่งบรรทัดตามด้านล่างนี้

smallrye.jwt.sign.key-location=private-key.pem
Enter fullscreen mode Exit fullscreen mode

ถือว่าอ้างอิง 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;
    }
}
Enter fullscreen mode Exit fullscreen mode

จบตามนี้ก็พร้อมรัน

ขั้นที่ 4 ทดสอบ

หา JWT Token ก่อน

$ ./mvnw quarkus:dev
Enter fullscreen mode Exit fullscreen mode

แล้วเรียก

$ curl -s http://localhost:8080/key
eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJlS042NTA2UzhlaXp5T2RYZzNUbFpoUUdhZEYxdWJFOSIsImlhdCI6MTYwMjUxMjIwMSwiZXhwIjoxNjAyNTEyNTAxLCJqdGkiOiIzSVpkYjRua0lBelJadW5YQ0dqQWVRIn0.M3KojWn78l9sAbcEqsekiKd-ukJktboRRs6CkrPz6JNfq03pSBTygu_Xf678Z45NGfTJy0wg6ftZj9x1h7GN4VBZ06nqm0Vqcmdx-st_thFswEVOrUlTxzNULMElOejJCybpYd2lwxA_3vYu_L5sj0fyvLfetev90zV0TDK8S91Xl_dfr75fo7hF1jPcdN_wmV3woB3YKhN3xV9zAJpn7F5o1oL2YaRutevVJMqGhidLgb1S8j0H8jB2smr3DZ7v8KNkkMvuCej0oU9f39BixNBsbgm7VnaWC-xoJqBGAEnSIeza00datSyp-NSZN-CB7gLISmunR3S_PQ4iIVGJCg
Enter fullscreen mode Exit fullscreen mode

เอาคีย์มาเรียกเข้าที่ Kong

เรียกแบบไม่ใส่ JWT Token

$ curl -s http://$HOST:8000/office/reports
{"message":"Unauthorized"}
Enter fullscreen mode Exit fullscreen mode

เรียกแบบใส่ 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" 
[]
Enter fullscreen mode Exit fullscreen mode

ถือว่าประสบความสำเร็จ รีบมาเขียนไว้นี่คือเก็บไว้ดูเผื่อลืม

ใครสนใจหรือมีคำถามมาคุยกันที่ twitter: @rawinng ครับ

Top comments (0)