JWT
JWT는 JSON Web Token(JWT) 인증 미들웨어를 반환합니다. 유효한 토큰의 경우 Ctx.Locals에 사용자를 설정하고 다음 핸들러를 호출합니다. 유효하지 않은 토큰의 경우 "401 - Unauthorized" 오류를 반환합니다. 토큰이 누락된 경우 "400 - Bad Request" 오류를 반환합니다.
Echo에 특별한 감사와 크레딧을 드립니다.
참고: Go 1.19 이상 버전 필요
Installation
이 미들웨어는 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
Signatures
jwtware.New(config ...jwtware.Config) func(*fiber.Ctx) error
Config
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
<source>:<name>
형식의 문자열로 사용
"header:Authorization"
AuthScheme
string
Authorization 헤더에 사용할 인증 스킴. 기본값("Bearer"
)은 기본 TokenLookup
값과 함께 사용될 때만 사용
"Bearer"
KeyFunc
func() jwt.Keyfunc
토큰 검증을 위한 공개 키를 제공하는 사용자 정의 함수
jwtKeyFunc
JWKSetURLs
[]string
JWT를 파싱하는 데 사용되는 고유한 JSON Web Key(JWK) 세트 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"
}
Authorization 요청 헤더의 토큰을 사용하여 제한된 리소스를 요청합니다.
curl localhost:3000/restricted -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE0NjE5NTcxMzZ9.RB3arc4-OyzASAaUhC2W3ReWaXAt_z2Fd3BN4aWTgEY"
응답
Welcome John Doe
RS256 예제
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
테스트와 동일하지만, RFC 7517에 명시된 JSON Web Key(JWK) 세트 형식의 유효한 공개 키 컬렉션에 대한 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("Unexpected jwt signing method=%v", t.Header["alg"])
}
// TODO 데이터베이스 등에서 서명 키를 로드하는 사용자 정의 구현
signingKey := "secret"
return []byte(signingKey), nil
}
}
Last updated