백엔드 Backend/Golang
[AUTH] Cookie를 이용한 검증
dongburiii
2025. 1. 21. 10:21
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
반응형