Fiber JWT
Middleware JWT zwraca middleware uwierzytelniające JSON Web Token (JWT). Dla poprawnego tokena ustawia użytkownika w Ctx.Locals i wywołuje następny handler. Dla nieprawidłowego tokenu zwraca błąd "401 - Nieautoryzowane". W przypadku braku tokenu zwraca błąd "400 - Złe żądanie".
Uwaga: Wymagana jest wersja Go 1.19 lub nowsza
Instalacja
To middleware obsługuje Fiber v1 i v2, zainstaluj odpowiednio.
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
Sygnatura
jwtware.New(config ...jwtware.Config) func(*fiber.Ctx) error
Konfiguracja
Właściwość | Typ | Opis | Domyślna wartość |
---|---|---|---|
Filter | func(*fiber.Ctx) bool |
Definiuje funkcję pomijającą middleware | nil |
SuccessHandler | func(*fiber.Ctx) error |
Definiuje funkcję do wykonania po prawidłowym tokenie | nil |
ErrorHandler | func(*fiber.Ctx, error) error |
Definiuje funkcję do wykonania po nieprawidłowym tokenie | 401 Nieprawidłowy lub wygasły JWT |
SigningKey | interface{} |
Klucz podpisujący używany do weryfikacji tokenu. Jeśli długość SigningKeys wynosi 0, jest używany jako zapasowy | nil |
SigningKeys | map[string]interface{} |
Odwzorowanie kluczy podpisujących używanych do weryfikacji tokenów z polem kid |
nil |
ContextKey | string |
Klucz kontekstu używany do przechowywania informacji o użytkowniku z tokenu w kontekście | "user" |
Claims | jwt.Claim |
Rozszerzone dane twierdzeń definiujące treść tokenu | jwt.MapClaims{} |
TokenLookup | string |
Ciąg w formacie : używany do analizy tokenu |
"header:Authorization" |
AuthScheme | string |
Schemat AuthScheme używany w nagłówku Autoryzacji. Domyślna wartość ("Bearer") jest używana tylko z domyślną wartością TokenLookup |
"Bearer" |
KeyFunc | func() jwt.Keyfunc |
Funkcja zdefiniowana przez użytkownika do dostarczania klucza publicznego do weryfikacji tokenu | jwtKeyFunc |
JWKSetURLs | []string |
Tablica unikalnych adresów URL zestawu kluczy JSON Web (JWK) używanych do analizy JWT | nil |
Przykład 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()
// Trasa logowania
app.Post("/login", login)
// Trasa dostępna bez uwierzytelniania
app.Get("/", accessible)
// Middleware JWT
app.Use(jwtware.New(jwtware.Config{
SigningKey: jwtware.SigningKey{Key: []byte("secret")},
}))
// Ograniczona trasa
app.Get("/restricted", restricted)
app.Listen(":3000")
}
func login(c *fiber.Ctx) error {
user := c.FormValue("user")
pass := c.FormValue("pass")
// Wyrzucanie błędu nieautoryzacji
if user != "john" || pass != "doe" {
return c.SendStatus(fiber.StatusUnauthorized)
}
// Tworzenie twierdzeń
claims := jwt.MapClaims{
"name": "John Doe",
"admin": true,
"exp": time.Now().Add(time.Hour * 72).Unix(),
}
// Tworzenie tokenu
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
// Generowanie zakodowanego tokenu i wysyłanie jako odpowiedź
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("Dostępne")
}
func restricted(c *fiber.Ctx) error {
user := c.Locals("user").(*jwt.Token)
claims := user.Claims.(jwt.MapClaims)
name := claims["name"].(string)
return c.SendString("Witaj " + name)
}
Test HS256
Zaloguj się za pomocą nazwy użytkownika i hasła, aby uzyskać token.
curl --data "user=john&pass=doe" http://localhost:3000/login
Odpowiedź
{
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE0NjE5NTcxMzZ9.RB3arc4-OyzASAaUhC2W3ReWaXAt_z2Fd3BN4aWTgEY"
}
Ogranicz dostęp do zasobu za pomocą tokenu w nagłówku autoryzacyjnym.
curl localhost:3000/restricted -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE0NjE5NTcxMzZ9.RB3arc4-OyzASAaUhC2W3ReWaXAt_z2Fd3BN4aWTgEY"
Odpowiedź
Witaj 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 (
// Oczywiście, jest to tylko przykładowy test. Nie rób tak w produkcji.
// W produkcji klucz prywatny należy wygenerować z wyprzedzeniem.
// Nigdy nie dodawaj klucza prywatnego do żadnego repozytorium GitHub.
privateKey *rsa.PrivateKey
)
func main() {
app := fiber.New()
// Dla celów demonstracyjnych generuj nową parę kluczy publiczny/prywatny za każdym razem, gdy program się uruchamia. Patrz notatka powyżej.
rng := rand.Reader
var err error
privateKey, err = rsa.GenerateKey(rng, 2048)
if err != nil {
log.Fatalf("rsa.GenerateKey:%v", err)
}
// Trasa logowania
app.Post("/login", login)
// Trasa bez uwierzytelniania
app.Get("/", accessible)
// Middleware JWT
app.Use(jwtware.New(jwtware.Config{
SigningKey: jwtware.SigningKey{
JWTAlg: jwtware.RS256,
Key: privateKey.Public(),
},
}))
// Ograniczona trasa
app.Get("/restricted", restricted)
app.Listen(":3000")
}
func login(c *fiber.Ctx) error {
user := c.FormValue("user")
pass := c.FormValue("pass")
// Wyrzuć błąd braku autoryzacji
if user != "john" || pass != "doe" {
return c.SendStatus(fiber.StatusUnauthorized)
}
// Utwórz dane
claims := jwt.MapClaims{
"name": "John Doe",
"admin": true,
"exp": time.Now().Add(time.Hour * 72).Unix(),
}
// Utwórz token
token := jwt.NewWithClaims(jwt.SigningMethodRS256, claims)
// Wygeneruj zakodowany token i wyślij go jako odpowiedź
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("Dostępny")
}
func restricted(c *fiber.Ctx) error {
user := c.Locals("user").(*jwt.Token)
claims := user.Claims.(jwt.MapClaims)
name := claims["name"].(string)
return c.SendString("Witaj " + name)
}
Test RS256
RS256 jest zasadniczo taki sam jak powyższy test HS256.
Test zestawu JWK
Te testy są takie same jak testy podstawowych JWT powyżej, jednak wymagają prawidłowych zestawów kluczy publicznych w formacie JWK Set pod JWKSetURLs.
Przykład niestandardowej funkcji KeyFunc
KeyFunc definiuje funkcję zdefiniowaną przez użytkownika, która służy do dostarczania kluczy publicznych do weryfikacji tokenów. Ta funkcja jest odpowiedzialna za weryfikację algorytmu sygnatury oraz wybór poprawnego klucza. Jeśli token jest wydany przez zewnętrzną stronę, niestandardowa funkcja KeyFunc może być przydatna.
Gdy została dostarczona niestandardowa funkcja KeyFunc, parametry SigningKey, SigningKeys oraz SigningMethod będą ignorowane. Jest to jedna z trzech opcji dostarczenia kluczy weryfikacyjnych tokenów. Kolejność priorytetów to: niestandardowa funkcja KeyFunc, SigningKeys oraz SigningKey. Jeśli żaden z parametrów SigningKeys ani SigningKey nie został dostarczony, ta funkcja musi zostać wskazana. Domyślnie dokonywana jest weryfikacja algorytmu sygnatury oraz wybór odpowiedniego klucza przy użyciu implementacji wewnętrznej.
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) {
// Sprawdź, czy metoda sygnatury jest poprawna
if t.Method.Alg() != jwtware.HS256 {
return nil, fmt.Errorf("Nieoczekiwana metoda sygnatury jwt=%v", t.Header["alg"])
}
// TODO Własna implementacja wczytywania klucza sygnatury, np. wczytaj z bazy danych
signingKey := "sekret"
return []byte(signingKey), nil
}
}