728x90
반응형

개요

  • 로그인 시 발급되는 access token을 쿠키에 세팅
  • 미들웨어에서 쿠키의 access token 을 검증

Cookie 세팅

  • 생성된 토큰을 쿠키에 세팅한다
func Login(res http.ResponseWriter, req *http.Request) {

    /*
        로그인 로직
    */

    accessTokenCookie := http.Cookie{
        Name: "accessToken",
        Value: accessToken,
        Path: "/",
        Secure: true, // HTTPS 만
        HttpOnly: true, // 브라우저에서 쿠키 조작 불가하게 세팅
        SameSite: http.SameSiteNoneMode,
    }

    refreshTokenCookie := http.Cookie{
        Name: "refreshToken",
        Value: refreshToken,
        Path: "/",
        Secure: true, // HTTPS 만
        HttpOnly: true, // 브라우저에서 쿠키 조작 불가하게 세팅
        SameSite: http.SameSiteNoneMode,
    }

    http.SetCookie(res, &accessTokenCookie)
    http.SetCookie(res, &refreshTokenCookie)

    /*
        응답 보내기
    */
}

검증 미들웨어

  • 쿠키에서 토큰을 추출해서 컨텍스트에 담는다
  • 담은 컨텍스트의 정보를 컨트롤러에서 사용한다.
// 사용자 정의 키 타입을 사용하여 컨텍스트 충돌 방지

type contextKey string

const (
    // JWT 서명에 사용할 비밀 키 (환경 변수로 관리하는 것이 좋습니다)

    // jwtSecret = configs.GlobalConfig.JwtKey // 실제 배포 시 환경 변수로 관리

    // 컨텍스트에 사용자 정보를 저장할 키
    userContextKey = contextKey("user")
)

// 사용자 정보 구조체

type User struct {
    UserId string
    UserEmail string
    UserStatus string
}

var excludeRouteList = []string{
    "/", "/api", 
    "/user/signup", "/user/login",
}

// AuthMiddleware는 accessToken 쿠키를 추출하고 JWT를 검증하는 미들웨어입니다.
func AuthMiddleware(next http.Handler) http.Handler {

    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {

        // 제외할 경로는 바로 다음 핸들러로 넘김
        for _, route := range excludeRouteList {
            if r.URL.Path == route {
                log.Printf("Found Match Route: %s", route)
                next.ServeHTTP(w, r)
                return
            }
        }

        // accessToken 쿠키 추출
        cookie, err := r.Cookie("accessToken")

        log.Printf("Cookie: %s", cookie)

        if err != nil {
            log.Printf("Get Cookie Error :%v", err)  

            if err == http.ErrNoCookie {
                response.Response(w, response.CommonResponseWithMessage{
                    Status: http.StatusUnauthorized,
                    Code: "AUTH001",
                    Message: "No access token provided",    
                })

                return
            }

            // 다른 쿠키 에러 처리

            response.Response(w, response.CommonResponseWithMessage{
                Status: http.StatusUnauthorized,
                Code: "AUTH002",
                Message: "Invalid cookie format",
            })

            return
        }

        accessToken := cookie.Value

        userId, userEmail, userStatus, validateErr := auth.ValidateJwtTokenFromString(accessToken)


        if validateErr != nil {
            log.Printf("ValidateErr Cookie Error :%v", validateErr)

            // 토큰 만료에 대한 응답
            if strings.Contains(validateErr.Error(), "token expired") {
                response.Response(w, response.CommonResponseWithMessage{
                    Status: http.StatusUnauthorized,
                    Code: "AUTH003",
                    Message: "Token expired",
                })

                return
            }

            // 일반적인 JWT 검증 실패 응답
            response.Response(w, response.CommonResponseWithMessage{
                Status: http.StatusUnauthorized,
                Code: "AUTH004",
                Message: "Invalid token",
            })

            return        
        }

        // 사용자 정보를 구조체로 생성

        user := User{
            UserId: userId,
            UserEmail: userEmail,
            UserStatus: userStatus,
        }

        // 사용자 정보를 컨텍스트에 추가
        ctx := context.WithValue(r.Context(), userContextKey, user)
        next.ServeHTTP(w, r.WithContext(ctx))
    })
}



// 사용자 정보를 핸들러 및 컨트롤러에서 가져오는 헬퍼 함수
func GetUserFromContext(ctx context.Context) (User, bool) {
    user, ok := ctx.Value(userContextKey).(User)
    return user, ok
}

컨트롤러에서 추출된 정보들 사용


func SampleController(res http.ResponseWriter, req *http.Request) {
    user, ok := middlewares.GetUserFromContext(req.Context())

    if !ok {    
        dto.SetErrorResponse(res, 401, "01", "JWT Verifying Error", nil)

        return
    }
    /*
        이후 로직
    */

    dto.SetResponse(res, 200, "01")
}
728x90
반응형
728x90
반응형

기존

  • 기존에는 직접 CORS 체크 미들웨어를 작성해 사용했다.
func CorsMiddlewares(next http.Handler) http.Handler {
    return http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) {

        origin := req.Header.Get("Origin")

        if origin == "" {
            origin = "unknown"
        }

        for _, o := range originList {
            if o == origin {    
                log.Printf("Allowed Origin: %s", origin)
                res.Header().Set("Access-Control-Allow-Origin", origin)
                res.Header().Set("Access-Control-Max-Age", "86400")
                res.Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS")
                res.Header().Set("Access-Control-Allow-Headers", "Content-Type, Authorization")
                res.Header().Set("Access-Control-Allow-Credentials", "true")
                break
            }
        }

        // Handle preflight request
        if req.Method == http.MethodOptions {
            res.WriteHeader(http.StatusOK)
            return
        }

        next.ServeHTTP(res, req)
    })
}

변경

  • 그러나 조금 더 심플하고 편하게 설정할 수 있는 패키지가 있어 가져와 사용했다.
get -u github.com/rs/cors
  • 이후 세팅은 이렇게 된다.
func CorsHanlder() *cors.Cors {
    corHandler := cors.New(cors.Options{
        AllowedOrigins: originList,
        AllowedMethods: []string{http.MethodGet, http.MethodPost, http.MethodOptions},
        AllowedHeaders: []string{"Origin", "Accept", "Content-Type", "Authorization"},
        AllowCredentials: true,
        MaxAge: 86400,        
        Debug: false,
    })

    return corHandler
}

미들웨어 등록하기

  • 현재 표준 라이브러리인 net/http 에서 gorilla/mux 로 변경한 상태이다.
func OpenServer() *http.Server {
    router := mux.NewRouter()

    routers.DefaultRouter(router)

    routers.UploadImageController(router)

    routers.AdminUserRouter(router)

    routers.AdminPostRouter(router)

    routers.UserRouter(router)

    routers.PostRouter(router)

    router.Use(middlewares.AuthMiddleware)

    // CORS 핸들러
    handler := middlewares.CorsHanlder().Handler(router)

    // 기존 방식  
    // handler := middlewares.CorsMiddlewares(router)

    serving := &http.Server{
        Handler: handler,
        Addr: configs.GlobalConfig.AppHost,
        WriteTimeout: 30 * time.Second,
        ReadTimeout: 30 * time.Second,
    }

    return serving
}
728x90
반응형

+ Recent posts