Fiber JWT
El middleware JWT devuelve un middleware de autenticación de Token Web JSON (JWT). Para un token válido, establece al usuario en Ctx.Locals y llama al siguiente controlador. Para un token no válido, devuelve un error "401 - No autorizado". Para un token faltante, devuelve un error "400 - Solicitud incorrecta".
Nota: Se requiere Go versión 1.19 o superior
Instalación
Este middleware es compatible con Fiber v1 y v2, por favor instale según corresponda.
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
Firma
jwtware.New(config ...jwtware.Config) func(*fiber.Ctx) error
Configuración
Propiedad | Tipo | Descripción | Valor por defecto |
---|---|---|---|
Filtro | func(*fiber.Ctx) bool |
Define una función para saltar el middleware | nil |
Controlador de éxito | func(*fiber.Ctx) error |
Define una función para ejecutar al tener un token válido | nil |
Controlador de error | func(*fiber.Ctx, error) error |
Define una función para ejecutar al tener un token inválido | 401 JWT no válido o caducado |
Clave de firma | interface{} |
La clave de firma usada para verificar el token. Si la longitud de SigningKeys es 0, se usa como respaldo | nil |
Claves de firma | map[string]interface{} |
El mapeo de claves de firma usadas para verificar tokens con un campo kid |
nil |
Clave de contexto | string |
La clave de contexto usada para almacenar la información del usuario a partir del token en el contexto | "user" |
Reclamaciones | jwt.Claim |
Los datos de reclamaciones extendidas que definen el contenido del token | jwt.MapClaims{} |
Búsqueda de token | string |
Una cadena en el formato : usada para analizar el token |
"header:Authorization" |
Esquema de autenticación | string |
El esquema de autenticación usado en el encabezado de Autorización. El valor predeterminado ("Bearer") se usa solo con el valor predeterminado de TokenLookup |
"Bearer" |
Función de clave | func() jwt.Keyfunc |
Una función definida por el usuario para proporcionar la clave pública para la verificación del token | jwtKeyFunc |
URL de JWKSet | []string |
Una lista de URL de conjunto de claves JSON Web (JWK) únicas utilizadas para analizar JWT | nil |
Ejemplo de 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()
// Ruta de inicio de sesión
app.Post("/login", login)
// Ruta accesible sin autenticación
app.Get("/", accessible)
// Middleware JWT
app.Use(jwtware.New(jwtware.Config{
SigningKey: jwtware.SigningKey{Key: []byte("secreto")},
}))
// Ruta restringida
app.Get("/restringido", restringido)
app.Listen(":3000")
}
func login(c *fiber.Ctx) error {
usuario := c.FormValue("usuario")
contraseña := c.FormValue("contraseña")
// Devolver error no autorizado
if usuario != "juan" || contraseña != "doe" {
return c.SendStatus(fiber.StatusUnauthorized)
}
// Crear reclamaciones
reclamaciones := jwt.MapClaims{
"nombre": "Juan Doe",
"admin": true,
"exp": time.Now().Add(time.Hour * 72).Unix(),
}
// Crear token
token := jwt.NewWithClaims(jwt.SigningMethodHS256, reclamaciones)
// Generar token codificado y enviar como respuesta
t, err := token.SignedString([]byte("secreto"))
if err != nil {
return c.SendStatus(fiber.StatusInternalServerError)
}
return c.JSON(fiber.Map{"token": t})
}
func accessible(c *fiber.Ctx) error {
return c.SendString("Accesible")
}
func restringido(c *fiber.Ctx) error {
usuario := c.Locals("user").(*jwt.Token)
reclamaciones := usuario.Claims.(jwt.MapClaims)
nombre := reclamaciones["nombre"].(string)
return c.SendString("Bienvenido " + nombre)
}
Prueba de HS256
Inicie sesión con nombre de usuario y contraseña para obtener un token.
curl --data "user=john&pass=doe" http://localhost:3000/login
Respuesta
{
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE0NjE5NTcxMzZ9.RB3arc4-OyzASAaUhC2W3ReWaXAt_z2Fd3BN4aWTgEY"
}
Request restricted resource using the token in the authorization header.
curl localhost:3000/restricted -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE0NjE5NTcxMzZ9.RB3arc4-OyzASAaUhC2W3ReWaXAt_z2Fd3BN4aWTgEY"
Respuesta
Bienvenido 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 (
// Obviamente, esto es solo un ejemplo de prueba. No debes hacer esto en producción.
// En producción, debes generar el par de claves de antemano.
// Nunca agregues la clave privada a ningún repositorio de GitHub.
privateKey *rsa.PrivateKey
)
func main() {
app := fiber.New()
// Para fines de demostración, genera un nuevo par de claves privada/pública cada vez que se ejecuta el programa. Consulta la nota anterior.
rng := rand.Reader
var err error
privateKey, err = rsa.GenerateKey(rng, 2048)
if err != nil {
log.Fatalf("rsa.GenerateKey:%v", err)
}
// Ruta de inicio de sesión
app.Post("/login", login)
// Ruta no autenticada
app.Get("/", accessible)
// Middleware JWT
app.Use(jwtware.New(jwtware.Config{
SigningKey: jwtware.SigningKey{
JWTAlg: jwtware.RS256,
Key: privateKey.Public(),
},
}))
// Ruta restringida
app.Get("/restricted", restricted)
app.Listen(":3000")
}
func login(c *fiber.Ctx) error {
user := c.FormValue("user")
pass := c.FormValue("pass")
// Lanzar error no autorizado
if user != "john" || pass != "doe" {
return c.SendStatus(fiber.StatusUnauthorized)
}
// Crear reclamaciones
claims := jwt.MapClaims{
"name": "John Doe",
"admin": true,
"exp": time.Now().Add(time.Hour * 72).Unix(),
}
// Crear token
token := jwt.NewWithClaims(jwt.SigningMethodRS256, claims)
// Generar token codificado y enviarlo como respuesta
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("Accesible")
}
func restricted(c *fiber.Ctx) error {
user := c.Locals("user").(*jwt.Token)
claims := user.Claims.(jwt.MapClaims)
name := claims["name"].(string)
return c.SendString("Bienvenido " + name)
}
Prueba de RS256
RS256 es esencialmente lo mismo que la prueba HS256 anterior.
Prueba de JWK Set
Estas pruebas son las mismas que las pruebas básicas de JWT anteriores, pero requieren conjuntos de claves públicas válidas en formato JWK Set en JWKSetURLs.
Ejemplo de Custom KeyFunc
La KeyFunc define una función definida por el usuario utilizada para proporcionar claves públicas para la validación del token. Esta función es responsable de validar el algoritmo de firma y seleccionar la clave correcta. Si el token es emitido por una parte externa, la KeyFunc definida por el usuario puede ser útil.
Cuando se proporciona una KeyFunc definida por el usuario, se ignorarán SigningKey, SigningKeys y SigningMethod. Esta es una de las tres opciones para proporcionar claves de validación de tokens. El orden de prioridad es KeyFunc definida por el usuario, SigningKeys y SigningKey. Si no se proporciona ni SigningKeys ni SigningKey, esta función debe ser proporcionada. El valor predeterminado es validar el algoritmo de firma y seleccionar la clave apropiada utilizando la implementación interna.
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) {
// Verificar si el método de firma es correcto
if t.Method.Alg() != jwtware.HS256 {
return nil, fmt.Errorf("Método de firma jwt inesperado=%v", t.Header["alg"])
}
// TODO Implementar la carga personalizada de la clave de firma, por ejemplo, cargar desde la base de datos
signingKey := "secreto"
return []byte(signingKey), nil
}
}