Fiber JWT

Das JWT-Middleware gibt ein JSON Web Token (JWT)-Authentifizierungsmiddleware zurück. Für ein gültiges Token setzt es den Benutzer in Ctx.Locals und ruft den nächsten Handler auf. Bei einem ungültigen Token gibt es einen "401 - Nicht autorisiert" Fehler zurück. Bei einem fehlenden Token gibt es einen "400 - Schlechte Anfrage" Fehler zurück.

Hinweis: Go-Version 1.19 oder höher ist erforderlich

Installation

Diese Middleware unterstützt Fiber v1 und v2, bitte entsprechend installieren.

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

Signatur

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

Konfiguration

Eigenschaft Typ Beschreibung Standardwert
Filter func(*fiber.Ctx) bool Definiert eine Funktion zum Überspringen der Middleware nil
SuccessHandler func(*fiber.Ctx) error Definiert eine Funktion zum Ausführen bei gültigem Token nil
ErrorHandler func(*fiber.Ctx, error) error Definiert eine Funktion zum Ausführen bei ungültigem Token 401 Ungültiges oder abgelaufenes JWT
SigningKey interface{} Der Signaturschlüssel, der zur Überprüfung des Tokens verwendet wird. Wenn die Länge von SigningKeys 0 ist, wird er als Fallback verwendet nil
SigningKeys map[string]interface{} Die Zuordnung von Signaturschlüsseln, die zur Überprüfung von Tokens mit einem kid-Feld verwendet werden nil
ContextKey string Der Kontextschlüssel, der zur Speicherung von Benutzerinformationen aus dem Token im Kontext verwendet wird "user"
Claims jwt.Claim Die erweiterten Anspruchsdaten, die den Tokeninhalt definieren jwt.MapClaims{}
TokenLookup string Ein String im Format :, der zum Parsen des Tokens verwendet wird "header:Authorization"
AuthScheme string Das in der Autorisierungs-Header verwendete AuthScheme. Der Standardwert ("Bearer") wird nur mit dem Standardwert TokenLookup verwendet "Bearer"
KeyFunc func() jwt.Keyfunc Eine benutzerdefinierte Funktion zur Bereitstellung eines öffentlichen Schlüssels für die Tokenüberprüfung jwtKeyFunc
JWKSetURLs []string Eine Liste von eindeutigen JSON Web Key (JWK) Set URLs, die zum Parsen von JWT verwendet werden nil

Beispiel von 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-Route
	app.Post("/login", login)

	// Zugängliche Route ohne Authentifizierung
	app.Get("/", accessible)

	// JWT-Middleware
	app.Use(jwtware.New(jwtware.Config{
		SigningKey: jwtware.SigningKey{Key: []byte("secret")},
	}))

	// Eingeschränkte Route
	app.Get("/restricted", restricted)

	app.Listen(":3000")
}

func login(c *fiber.Ctx) error {
	user := c.FormValue("user")
	pass := c.FormValue("pass")

	// Fehler bei Nichtautorisiertem
	if user != "john" || pass != "doe" {
		return c.SendStatus(fiber.StatusUnauthorized)
	}

	// Ansprüche erstellen
	claims := jwt.MapClaims{
		"name":  "John Doe",
		"admin": true,
		"exp":   time.Now().Add(time.Hour * 72).Unix(),
	}

	// Token erstellen
	token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)

	// Codierten Token generieren und als Antwort senden
	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("Zugänglich")
}

func restricted(c *fiber.Ctx) error {
	user := c.Locals("user").(*jwt.Token)
	claims := user.Claims.(jwt.MapClaims)
	name := claims["name"].(string)
	return c.SendString("Willkommen " + name)
}

HS256 Test

Melden Sie sich mit Benutzername und Passwort an, um ein Token zu erhalten.

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

Antwort

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

Anfrage für eingeschränkte Ressourcen unter Verwendung des Tokens im Autorisierungsheader.

curl localhost:3000/restricted -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE0NjE5NTcxMzZ9.RB3arc4-OyzASAaUhC2W3ReWaXAt_z2Fd3BN4aWTgEY"

Antwort

Willkommen 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 (
	// Offensichtlich handelt es sich hier nur um ein Testbeispiel. Machen Sie dies nicht in der Produktion.
	// In der Produktion sollten Sie das Schlüsselpaar im Voraus generieren.
	// Fügen Sie niemals den privaten Schlüssel zu einem GitHub-Repository hinzu.
	privateKey *rsa.PrivateKey
)

func main() {
	app := fiber.New()

	// Generieren Sie zum Zweck der Demonstration jedes Mal, wenn das Programm ausgeführt wird, ein neues privates/öffentliche Schlüsselpaar. Siehe die obige Anmerkung.
	rng := rand.Reader
	var err error
	privateKey, err = rsa.GenerateKey(rng, 2048)
	if err != nil {
		log.Fatalf("rsa.GenerateKey:%v", err)
	}

	// Login-Route
	app.Post("/login", login)

	// Unauthentifizierte Route
	app.Get("/", accessible)

	// JWT-Middleware
	app.Use(jwtware.New(jwtware.Config{
		SigningKey: jwtware.SigningKey{
			JWTAlg: jwtware.RS256,
			Key:    privateKey.Public(),
		},
	}))

	// Eingeschränkte Route
	app.Get("/restricted", restricted)

	app.Listen(":3000")
}

func login(c *fiber.Ctx) error {
	user := c.FormValue("user")
	pass := c.FormValue("pass")

	// Werfen Sie einen unbefugten Fehler
	if user != "john" || pass != "doe" {
		return c.SendStatus(fiber.StatusUnauthorized)
	}

	// Erstellen von Ansprüchen
	claims := jwt.MapClaims{
		"name":  "John Doe",
		"admin": true,
		"exp":   time.Now().Add(time.Hour * 72).Unix(),
	}

	// Erstellen des Tokens
	token := jwt.NewWithClaims(jwt.SigningMethodRS256, claims)

	// Generieren des codierten Tokens und Senden als Antwort
	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("Erreichbar")
}

func restricted(c *fiber.Ctx) error {
	user := c.Locals("user").(*jwt.Token)
	claims := user.Claims.(jwt.MapClaims)
	name := claims["name"].(string)
	return c.SendString("Willkommen " + name)
}

RS256 Test

RS256 ist im Wesentlichen dasselbe wie der obige HS256-Test.

JWK-Set-Test

Diese Tests sind wie die grundlegenden JWT-Tests oben, erfordern jedoch gültige öffentliche Schlüsselsätze im JWK-Set-Format unter JWKSetURLs.

Beispiel für benutzerdefinierte KeyFunc

Die KeyFunc definiert eine benutzerdefinierte Funktion, die verwendet wird, um öffentliche Schlüssel für die Tokenvalidierung bereitzustellen. Diese Funktion ist dafür verantwortlich, den Signaturalgorithmus zu validieren und den richtigen Schlüssel auszuwählen. Wenn das Token von einer externen Partei ausgestellt wird, kann die benutzerdefinierte KeyFunc nützlich sein.

Wenn eine benutzerdefinierte KeyFunc bereitgestellt wird, werden SigningKey, SigningKeys und SigningMethod ignoriert. Dies ist eine von drei Optionen zur Bereitstellung von Tokenvalidierungsschlüsseln. Die Prioritätsreihenfolge ist die benutzerdefinierte KeyFunc, SigningKeys und SigningKey. Wenn weder SigningKeys noch SigningKey bereitgestellt werden, muss diese Funktion bereitgestellt werden. Standardmäßig wird der Signaturalgorithmus validiert und der geeignete Schlüssel mithilfe der internen Implementierung ausgewählt.

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) {
		// Überprüfen, ob die Signaturmethode korrekt ist
		if t.Method.Alg() != jwtware.HS256 {
			return nil, fmt.Errorf("Unerwartete jwt-Signaturmethode=%v", t.Header["alg"])
		}

		// TODO: Implementierung der benutzerdefinierten Ladung des Signaturschlüssels, z. B. Laden aus der Datenbank
    signingKey := "geheim"

		return []byte(signingKey), nil
	}
}