Fiber JWT

JWT 미들웨어는 JSON Web Token (JWT) 인증 미들웨어를 반환합니다. 유효한 토큰의 경우 사용자를 Ctx.Locals에 설정하고 다음 핸들러를 호출합니다. 유효하지 않은 토큰의 경우 "401 - Unauthorized" 오류를 반환하며, 누락된 토큰의 경우 "400 - Bad Request" 오류를 반환합니다.

참고: 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 Invalid or expired JWT
SigningKey interface{} 토큰 검증에 사용되는 서명 키. SigningKeys의 길이가 0인 경우, 대체로 사용됨 nil
SigningKeys map[string]interface{} kid 필드가 있는 토큰을 확인하기 위해 사용되는 서명 키의 매핑 nil
ContextKey string 컨텍스트 내에서 토큰으로부터 사용자 정보를 저장하는 데 사용되는 컨텍스트 키 "user"
Claims jwt.Claim 토큰 내용을 정의하는 확장된 클레임 데이터 jwt.MapClaims{}
TokenLookup string 토큰을 구문 분석하는 데 사용되는 : 형식의 문자열 "header:Authorization"
AuthScheme string Authorization 헤더에서 사용되는 AuthScheme. 기본값("Bearer")은 기본 TokenLookup 값과 함께만 사용됨 "Bearer"
KeyFunc func() jwt.Keyfunc 토큰 확인을 위해 공개 키를 제공하기 위한 사용자 정의 함수 jwtKeyFunc
JWKSetURLs []string JWT를 구문 분석하는 데 사용되는 고유한 JSON Web Key (JWK) Set URL 슬라이스 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("Accessible")
}

func restricted(c *fiber.Ctx) error {
	user := c.Locals("user").(*jwt.Token)
	claims := user.Claims.(jwt.MapClaims)
	name := claims["name"].(string)
	return c.SendString("Welcome " + 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("Accessible")
}

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

RS256 테스트

RS256는 기본 HS256 테스트와 본질적으로 동일합니다.

JWK 세트 테스트

이러한 테스트들은 위의 기본 JWT 테스트와 동일하지만, JWK Set 형식의 유효한 공개 키 세트가 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) {
		// 서명 방법이 올바른지 확인
		if t.Method.Alg() != jwtware.HS256 {
			return nil, fmt.Errorf("예기치 않은 jwt 서명 방법=%v", t.Header["alg"])
		}

		// TODO 서명 키의 사용자 정의 로딩 구현, 예를 들어 데이터베이스에서 로드
    signingKey := "비밀키"

		return []byte(signingKey), nil
	}
}