جي دبليو تي

يعيد جي دبليو تي طبقة وسيطة لمصادقة رمز مفتاح JSON (JWT). بالنسبة للرمز الصالح، يضبط المستخدم في Ctx.Locals ويُشغّل المعالج التالي. أما بالنسبة للرمز غير الصالح، فإنه يُعيد خطأ "401 - غير مصرح". وبالنسبة للرمز المفقود، فإنه يُعيد خطأ "400 - طلب سيء".

ملاحظة: يتطلب Go الإصدار 1.19 أو أحدث

التثبيت

هذه الطبقة الوسيطة تدعم 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

التوقيع

jwtware.New(config ...jwtware.Config) func(*fiber.Ctx) error

الاعدادات

الخاصية النوع الوصف القيمة الافتراضية
Filter func(*fiber.Ctx) bool يحدد وظيفة لتخطي الطبقة الوسيطة nil
SuccessHandler func(*fiber.Ctx) error يحدد وظيفة للتنفيذ عندما يكون الرمز صالح nil
ErrorHandler func(*fiber.Ctx, error) error يحدد وظيفة للتنفيذ عندما يكون الرمز غير صالح 401 رمز JWT غير صالح أو منتهي الصلاحية
SigningKey interface{} مفتاح التوقيع المستخدم للتحقق من الرمز. إذا كانت طول مفاتيح التوقيع 0، يتم استخدامه كقيمة احتياطية nil
SigningKeys map[string]interface{} إعداد مفاتيح التوقيع المستخدمة للتحقق من الرموز بحقل kid nil
ContextKey string المفتاح السياقي المستخدم لتخزين معلومات المستخدم من الرمز في السياق "user"
Claims jwt.Claim بيانات المطالبات الموسعة التي تحدد محتوى الرمز jwt.MapClaims{}
TokenLookup string سلسلة في الصيغة : تستخدم لتحليل الرمز "header:Authorization"
AuthScheme string مخطط الاعتماد المستخدم في رأس الاعتماد. يُستخدم القيمة الافتراضية ("Bearer") فقط مع القيمة الافتراضية لـ TokenLookup "Bearer"
KeyFunc func() jwt.Keyfunc وظيفة محددة من قبل المستخدم لتوفير المفتاح العام للتحقق من الرمز jwtKeyFunc
JWKSetURLs []string مجموعة فريدة من عناوين الويب لمفاتيح JSON (JWK) المستخدمة لتحليل 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()

	// مسار تسجيل الدخول
	app.Post("/login", login)

	// مسار قابل للوصول دون مصادقة
	app.Get("/", accessible)

	// طبقة وسيطة JWT
	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 := jwt.MapClaims{
		"name":  "John Doe",
		"admin": true,
		"exp":   time.Now().Add(time.Hour * 72).Unix(),
	}

	// إنشاء الرمز
	token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)

	// إنشاء الرمز المشفر وإرساله كاستجابة
	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("قابل للوصول")
}

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)
}

اختبار HS256

تسجيل الدخول بواسطة اسم المستخدم وكلمة المرور للحصول على رمز.

curl --data "user=john&pass=doe" http://localhost:3000/login

الاستجابة

{
  "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE0NjE5NTcxMzZ9.RB3arc4-OyzASAaUhC2W3ReWaXAt_z2Fd3BN4aWTgEY"
}

طلب مورد مقيد باستخدام الرمز في رأس التفويض.

curl localhost:3000/restricted -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIأنا سعيد للمساعدة.ınR5cCI6IkpXVCJ9.eyJleHAiOjE0NjE5NTcxMzZ9.RB3arc4-OyzASAaUhC2W3ReWaXAt_z2Fd3BN4aWTgEY"

الاستجابة

مرحبا جون دو
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
	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

هذه الاختبارات هي نفس اختبارات JWT الأساسية أعلاه، ولكنها تتطلب مجموعات مفاتيح عامة صالحة بتنسيق مجموعة JWK في عناوين 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) {
		// Check if the signature method is correct
		if t.Method.Alg() != jwtware.HS256 {
			return nil, fmt.Errorf("خطأ في طريقة توقيع jwt، alg=%v غير متوقع", t.Header["alg"])
		}

		// قم بتنفيذ تحميل مخصص للمفتاح التوقيع، على سبيل المثال، قم بتحميله من قاعدة البيانات
    signingKey := "سري"

		return []byte(signingKey), nil
	}
}