소개
이 글에서는 Golang의 Gin 웹 프레임워크를 사용하여 JWT(JSON Web Token) 기반의 인증 시스템을 구현하는 방법을 알아보겠습니다.
필요한 패키지
go get -u github.com/gin-gonic/gin
go get -u github.com/golang-jwt/jwt/v5
go get -u github.com/joho/godotenv
|
프로젝트 구조
├── .env
├── main.go
├── middleware
│ └── auth.go
├── models
│ └── user.go
└── handlers
└── auth.go
|
환경 변수 설정
.env 파일을 생성하고 JWT 시크릿 키를 설정합니다:
JWT_SECRET=your-secret-key
|
사용자 모델 정의
models/user.go:
package models
type User struct {
ID uint `json:"id"`
Username string `json:"username"`
Password string `json:"password"`
}
|
JWT 미들웨어 구현
middleware/auth.go:
package middleware
import (
"net/http"
"os"
"strings"
"github.com/gin-gonic/gin"
"github.com/golang-jwt/jwt/v5"
)
func AuthMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
authHeader := c.GetHeader("Authorization")
if authHeader == "" {
c.JSON(http.StatusUnauthorized, gin.H{"error": "Authorization header is required"})
c.Abort()
return
}
bearerToken := strings.Split(authHeader, " ")
if len(bearerToken) != 2 {
c.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid token format"})
c.Abort()
return
}
tokenString := bearerToken[1]
token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
return []byte(os.Getenv("JWT_SECRET")), nil
})
if err != nil || !token.Valid {
c.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid token"})
c.Abort()
return
}
claims, ok := token.Claims.(jwt.MapClaims)
if !ok {
c.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid token claims"})
c.Abort()
return
}
c.Set("user_id", claims["user_id"])
c.Next()
}
}
|
인증 핸들러 구현
handlers/auth.go:
package handlers
import (
"net/http"
"os"
"time"
"github.com/gin-gonic/gin"
"github.com/golang-jwt/jwt/v5"
"your-project/models"
)
func Login(c *gin.Context) {
var user models.User
if err := c.ShouldBindJSON(&user); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
// 여기에서 실제 데이터베이스 검증 로직을 구현해야 합니다
// 예시를 위해 하드코딩된 검증을 사용합니다
if user.Username != "test" || user.Password != "password" {
c.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid credentials"})
return
}
// JWT 토큰 생성
token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
"user_id": 1,
"exp": time.Now().Add(time.Hour * 24).Unix(),
})
tokenString, err := token.SignedString([]byte(os.Getenv("JWT_SECRET")))
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "Could not generate token"})
return
}
c.JSON(http.StatusOK, gin.H{
"token": tokenString,
})
}
func Protected(c *gin.Context) {
userID, exists := c.Get("user_id")
if !exists {
c.JSON(http.StatusUnauthorized, gin.H{"error": "Unauthorized"})
return
}
c.JSON(http.StatusOK, gin.H{
"message": "This is a protected route",
"user_id": userID,
})
}
|
메인 애플리케이션 구현
main.go:
package main
import (
"log"
"github.com/gin-gonic/gin"
"github.com/joho/godotenv"
"your-project/handlers"
"your-project/middleware"
)
func main() {
if err := godotenv.Load(); err != nil {
log.Fatal("Error loading .env file")
}
r := gin.Default()
// 공개 라우트
r.POST("/login", handlers.Login)
// 보호된 라우트
protected := r.Group("/api")
protected.Use(middleware.AuthMiddleware())
{
protected.GET("/protected", handlers.Protected)
}
r.Run(":8080")
}
|
사용 방법
로그인
curl -X POST http://localhost:8080/login \
-H "Content-Type: application/json" \
-d '{"username": "test", "password": "password"}'
|
보호된 엔드포인트 접근
curl http://localhost:8080/api/protected \
-H "Authorization: Bearer YOUR_JWT_TOKEN"
|
보안 고려사항
- 실제 프로덕션 환경에서는 안전한 비밀번호 해싱을 구현해야 합니다.
- 토큰 만료 시간을 적절히 설정해야 합니다.
- HTTPS를 사용하여 통신을 암호화해야 합니다.
- 토큰 블랙리스트 구현을 고려해야 합니다.
결론
이 예제에서는 Gin 프레임워크와 JWT를 사용하여 기본적인 인증 시스템을 구현해보았습니다. 실제 프로덕션 환경에서는 추가적인 보안 조치와 에러 처리가 필요합니다.