توکن JSON Web Token (JWT)

میان‌افزار JWT توکن احراز هویت JSON Web Token (JWT) را برمی‌گرداند. برای یک توکن معتبر، کاربر را در Ctx.Locals قرار می‌دهد و Handler بعدی را فراخوانی می‌کند. برای یک توکن نامعتبر، خطا "401 - غیرمجاز" را بر می‌گرداند. برای یک توکن گمشده، خطا "400 - درخواست نادرست" را بر می‌گرداند.

توجه: نسخه ۱.۱۹ Go یا بالاتر مورد نیاز است

نصب

این میان‌افزار 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{} کلید امضا برای تأیید توکن. اگر طول SigningKeys برابر ۰ باشد، به‌عنوان یک نقش برمی‌گردد nil
SigningKeys map[string]interface{} نگاشت کلیدهای امضا برای تأیید توکن‌ها با یک فیلد kid nil
ContextKey string کلید متناوب از کاربر برای ذخیره اطلاعات توکن در متناوب "user"
Claims jwt.Claim داده‌های ادعاهای توکن گسترش یافته که محتوای توکن را تعیین می‌کند jwt.MapClaims{}
TokenLookup string یک رشته با فرمت : برای تجزیه توکن استفاده می‌شود "header:Authorization"
AuthScheme string AuthScheme مورد استفاده در هدر Authorization. مقدار پیش‌فرض ("Bearer") فقط با مقدار پیش‌فرض TokenLookup استفاده می‌شود "Bearer"
KeyFunc func() jwt.Keyfunc تابع تعریف‌شده توسط کاربر برای ارائه کلید عمومی برای تأیید توکن jwtKeyFunc
JWKSetURLs []string یک آرایه از URLهای یکتای مجموعه کلید JSON Web (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("رمز")},
	}))

	// مسیر محدود شده
	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":  "جان دو",
		"admin": true,
		"exp":   time.Now().Add(time.Hour * 72).Unix(),
	}

	// ایجاد توکن
	token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)

	// تولید توکن رمزنگاری شده و ارسال به عنوان پاسخ
	t, err := token.SignedString([]byte("رمز"))
	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 eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE0NjE5NTcxMzZ9.RB3arc4-OyzASAaUhC2W3ReWaXAt_z2Fd3BN4aWTgEY"

پاسخ

Welcome 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
	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 تعریف شده توسط کاربر ارائه شود، 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 := "رمز"

		return []byte(signingKey), nil
	}
}