Fiber JWT
Middleware JWT คืนค่า JSON Web Token (JWT) authentication middleware ในกรณีที่มี token ที่ถูกต้อง จะตั้งค่า user ใน Ctx.Locals และเรียก handler ถัดไป สำหรับ token ที่ไม่ถูกต้อง จะคืนค่า "401 - Unauthorized" error สำหรับ token ที่หายไป จะคืนค่า "400 - Bad Request" error
หมายเหตุ: จำเป็นต้องใช้ Go เวอร์ชัน 1.19 หรือสูงกว่า
การติดตั้ง
Middleware นี้รองรับ Fiber v1 และ v2 โปรดติดตั้งตามนี้
go get -u github.com/gofiber/fiber/v2
go get -u github.com/gofiber/contrib/jwt
go get -u github.com/golang-jwt/jwt/v5
Signature
jwtware.New(config ...jwtware.Config) func(*fiber.Ctx) error
การกำหนดค่า
Property | Type | คำอธิบาย | ค่าเริ่มต้น |
---|---|---|---|
Filter | func(*fiber.Ctx) bool |
กำหนดฟังก์ชันเพื่อข้าม middleware | nil |
SuccessHandler | func(*fiber.Ctx) error |
กำหนดฟังก์ชันเพื่อดำเนินการเมื่อ token ถูกต้อง | nil |
ErrorHandler | func(*fiber.Ctx, error) error |
กำหนดฟังก์ชันเพื่อดำเนินการเมื่อ token ไม่ถูกต้อง | 401 Invalid or expired JWT |
SigningKey | interface{} |
คีย์ที่ใช้ในการยืนยัน token หากความยาวของ SigningKeys เป็น 0 จะใช้เป็น fallback | nil |
SigningKeys | map[string]interface{} |
การแมปคีย์ของ คีย์ที่ใช้ในการยืนยัน token พร้อมกับฟิลด์ kid |
nil |
ContextKey | string |
คีย์ของ context ที่ใช้เก็บข้อมูลผู้ใช้จาก token ใน context | "user" |
Claims | jwt.Claim |
ข้อมูลเพิ่มเติมของ claims ที่กำหนดรูปแบบของ token | jwt.MapClaims{} |
TokenLookup | string |
สตริงในรูปแบบ : ที่ใช้ในการแยก token |
"header:Authorization" |
AuthScheme | string |
AuthScheme ที่ใช้ในหัวข้อ Authorization header ค่าเริ่มต้น ("Bearer") จะใช้กับค่าเริ่มต้นของ TokenLookup เท่านั้น |
"Bearer" |
KeyFunc | func() jwt.Keyfunc |
ฟังก์ชันที่กำหนดโดยผู้ใช้เพื่อให้คีย์สาธารณะสำหรับการยืนยัน token | jwtKeyFunc |
JWKSetURLs | []string |
ลิสต์ของ unique JSON Web Key (JWK) Set URLs ที่ใช้ในการแยก JWT | nil |
ตัวอย่างของ HS256
package main
import (
"time"
"github.com/gofiber/fiber/v2"
jwtware "github.com/gofiber/contrib/jwt"
"github.com/golang-jwt/jwt/v5"
)
func main() {
app := fiber.New()
// เส้นทาง Login
app.Post("/login", login)
// เส้นทางที่เข้าถึงได้โดยไม่ต้องการการพิสูจน์ตัวตน
app.Get("/", accessible)
// JWT middleware
app.Use(jwtware.New(jwtware.Config{
SigningKey: jwtware.SigningKey{Key: []byte("secret")},
}))
// เส้นทางที่จำกัดสิทธิ์
app.Get("/restricted", restricted)
app.Listen(":3000")
}
func login(c *fiber.Ctx) error {
user := c.FormValue("user")
pass := c.FormValue("pass")
// ลักษณะของคำผิดอำนาจ
if user != "john" || pass != "doe" {
return c.SendStatus(fiber.StatusUnauthorized)
}
// สร้าง claims
claims := jwt.MapClaims{
"name": "John Doe",
"admin": true,
"exp": time.Now().Add(time.Hour * 72).Unix(),
}
// สร้าง token
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
// สร้าง token ที่เข้ารหัสและส่งเป็นตอบกลับ
t, err := token.SignedString([]byte("secret"))
if err != nil {
return c.SendStatus(fiber.StatusInternalServerError)
}
return c.JSON(fiber.Map{"token": t})
}
func accessible(c *fiber.Ctx) error {
return c.SendString("Accessible")
}
func restricted(c *fiber.Ctx) error {
user := c.Locals("user").(*jwt.Token)
claims := user.Claims.(jwt.MapClaims)
name := claims["name"].(string)
return c.SendString("Welcome " + name)
}
ทดสอบ HS256
เข้าสู่ระบบด้วยชื่อผู้ใช้และรหัสผ่านเพื่อรับโทเค็น
curl --data "user=john&pass=doe" http://localhost:3000/login
การตอบรับ
{
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE0NjE5NTcxMzZ9.RB3arc4-OyzASAaUhC2W3ReWaXAt_z2Fd3BN4aWTgEY"
}
ขอข้อมูลจำกัดโดยใช้โทเค็นในส่วนหัวการอนุญาต
curl localhost:3000/restricted -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE0NjE5NTcxMzZ9.RB3arc4-OyzASAaUhC2W3ReWaXAt_z2Fd3BN4aWTgEY"
การตอบรับ
ยินดีต้อนรับ John Doe
package main
import (
"crypto/rand"
"crypto/rsa"
"log"
"time"
"github.com/gofiber/fiber/v2"
"github.com/golang-jwt/jwt/v5"
jwtware "github.com/gofiber/contrib/jwt"
)
var (
// อย่างแน่นอนว่านี้เป็นตัวอย่างการทดสอบเท่านั้น อย่าทำเช่นนี้ในการใช้งานจริง
// ในการใช้งานจริงคุณควรสร้างคู่กุญแจล่วงหน้า
// อย่าเพิ่มกุญแจส่วนตัวลงในที่เก็บข้อมูลของ GitHub
privateKey *rsa.PrivateKey
)
func main() {
app := fiber.New()
// เพื่อการสาธิต สร้างคู่กุญแจส่วนตัว/สาธิตใหม่ทุกครั้งที่โปรแกรมทำงาน ดูหมายเหตุข้างต้น
rng := rand.Reader
var err error
privateKey, err = rsa.GenerateKey(rng, 2048)
if err != nil {
log.Fatalf("rsa.GenerateKey:%v", err)
}
// เส้นทางล็อกอิน
app.Post("/login", login)
// เส้นทางที่ไม่ได้รับการรับรอง
app.Get("/", accessible)
// มีการใช้ JWT middleware
app.Use(jwtware.New(jwtware.Config{
SigningKey: jwtware.SigningKey{
JWTAlg: jwtware.RS256,
Key: privateKey.Public(),
},
}))
// เส้นทางที่ถูกจำกัด
app.Get("/restricted", restricted)
app.Listen(":3000")
}
func login(c *fiber.Ctx) error {
user := c.FormValue("user")
pass := c.FormValue("pass")
// โยนข้อผิดพลาดการไม่อนุญาต
if user != "john" || pass != "doe" {
return c.SendStatus(fiber.StatusUnauthorized)
}
// สร้างข้อตั้ง
claims := jwt.MapClaims{
"name": "John Doe",
"admin": true,
"exp": time.Now().Add(time.Hour * 72).Unix(),
}
// สร้างโทเค็น
token := jwt.NewWithClaims(jwt.SigningMethodRS256, claims)
// สร้างโทเค็นที่เข้ารหัสแล้วและส่งมันเป็นการตอบรับ
t, err := token.SignedString(privateKey)
if err != nil {
log.Printf("token.SignedString:%v", err)
return c.SendStatus(fiber.StatusInternalServerError)
}
return c.JSON(fiber.Map{"token": t})
}
func accessible(c *fiber.Ctx) error {
return c.SendString("สามารถเข้าถึงได้")
}
func restricted(c *fiber.Ctx) error {
user := c.Locals("user").(*jwt.Token)
claims := user.Claims.(jwt.MapClaims)
name := claims["name"].(string)
return c.SendString("ยินดีต้อนรับ " + name)
}
ทดสอบ RS256
RS256 ก็คืออย่างเดียวกับทดสอบ HS256 ข้างต้น
ทดสอบ JWK Set
ทดสอบเหล่านี้เหมือนกับทดสอบ JWT พื้นฐานข้างต้น แต่ต้องการเซตกุญแจสาธิตที่ถูกต้องในรูปแบบ JWK Set ที่ JWKSetURLs
ตัวอย่าง KeyFunc ที่กำหนดเอง
KeyFunc กำหนดฟังก์ชั่นที่กำหนดโดยผู้ใช้เพื่อให้คีย์สาธารณะสำหรับการตรวจสอบโทเค็น ฟังก์ชั่นนี้รับผิดชอบในการตรวจสอบวิธีการลงลายและเลือกคีย์ที่ถูกต้อง หากโทเค็นถูกออกโดยฝ่ายภายนอก การกำหนด KeyFunc ที่ผู้ใช้กำหนดอาจจะมีประโยชน์
เมื่อกำหนด KeyFunc ที่ผู้ใช้กำหนดไว้ ค่า SigningKey, SigningKeys, และ SigningMethod จะถูกละเว้น นี่เป็นหนึ่งในทางเลือกสามตัวสำหรับการกำหนดคีย์ตรวจสอบโทเค็น ลำดับความสำคัญคือ KeyFunc ที่ผู้ใช้กำหนด, SigningKeys, และ SigningKey หากไม่ได้กำหนด SigningKeys หรือ SigningKey ฟังก์ชั่นนี้ต้องได้รับการกำหนดไว้ ค่าเริ่มต้นคือการตรวจสอบวิธีการลงลายและเลือกคีย์ที่เหมาะสมโดยการดำเนินการภายใน
package main
import (
"fmt"
"github.com/gofiber/fiber/v2"
jwtware "github.com/gofiber/contrib/jwt"
"github.com/golang-jwt/jwt/v5"
)
func main() {
app := fiber.New()
app.Use(jwtware.New(jwtware.Config{
KeyFunc: customKeyFunc(),
}))
app.Get("/ok", func(c *fiber.Ctx) error {
return c.SendString("OK")
})
}
func customKeyFunc() jwt.Keyfunc {
return func(t *jwt.Token) (interface{}, error) {
// ตรวจสอบว่าวิธีการลงลายถูกต้องหรือไม่
if t.Method.Alg() != jwtware.HS256 {
return nil, fmt.Errorf("วิธีการลงลายของ jwt ไม่คาดคะเน=%v", t.Header["alg"])
}
// ทำการดำเนินการโหลดคีย์ตรวจสอบที่กำหนดเอง เช่น โหลดจากฐานข้อมูล
signingKey := "secret"
return []byte(signingKey), nil
}
}