From 215bdf01dfadecfbd5b12f79cfa0eafe35e9f721 Mon Sep 17 00:00:00 2001 From: Kwoun Ki Ho <73146678+Chocochip101@users.noreply.github.com> Date: Thu, 24 Oct 2024 10:32:05 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20log=20=EC=84=A4=EC=A0=95=20(#925)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/cruru/auth/service/AuthService.java | 36 +++++++++++++-- .../global/AuthenticationInterceptor.java | 36 ++++++++++++--- .../com/cruru/global/util/CookieManager.java | 44 +++++++++++++++---- 3 files changed, 100 insertions(+), 16 deletions(-) diff --git a/backend/src/main/java/com/cruru/auth/service/AuthService.java b/backend/src/main/java/com/cruru/auth/service/AuthService.java index d487d8a54..2b31d2908 100644 --- a/backend/src/main/java/com/cruru/auth/service/AuthService.java +++ b/backend/src/main/java/com/cruru/auth/service/AuthService.java @@ -12,12 +12,13 @@ import com.cruru.member.domain.MemberRole; import io.jsonwebtoken.Claims; import io.jsonwebtoken.ExpiredJwtException; -import java.util.HashMap; import java.util.Map; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +@Slf4j @Service @Transactional(readOnly = true) @RequiredArgsConstructor @@ -32,63 +33,78 @@ public class AuthService { private final TokenRedisClient tokenRedisClient; public Token createAccessToken(String email, MemberRole role) { + log.info("Creating access token for email: {}, role: {}", email, role); Map claims = getClaims(email, role); String token = tokenProvider.createToken(claims, tokenProperties.accessExpireLength()); + log.debug("Access token created: {}", token); return new AccessToken(token); } @Transactional public Token createRefreshToken(String email, MemberRole role) { + log.info("Creating refresh token for email: {}", email); if (tokenRedisClient.existsByEmail(email)) { + log.debug("Existing refresh token found, rotating token"); return rotateRefreshToken(email, role); } Map claims = getClaims(email, role); String token = tokenProvider.createToken(claims, tokenProperties.refreshExpireLength()); tokenRedisClient.saveToken(email, token); + log.debug("New refresh token created and saved to Redis: {}", token); return new RefreshToken(token, email); } @Transactional public TokenResponse refresh(String refreshToken) { + log.info("Refreshing tokens using refresh token"); String email = extractEmail(refreshToken); checkRefreshTokenExists(email, refreshToken); MemberRole role = MemberRole.valueOf(extractMemberRole(refreshToken)); validMemberRefreshToken(refreshToken, email); + log.debug("Refresh token valid, rotating tokens"); return rotateTokens(email, role); } private void checkRefreshTokenExists(String email, String refreshToken) { + log.info("Checking if refresh token exists for email: {}", email); if (!isTokenSignatureValid(refreshToken)) { + log.error("Refresh token signature is invalid for token: {}", refreshToken); throw new IllegalTokenException(); } if (!tokenRedisClient.existsByToken(email, refreshToken)) { + log.error("Refresh token does not exist in Redis for email: {}", email); throw new IllegalTokenException(); } if (isTokenExpired(refreshToken)) { + log.warn("Refresh token expired for email: {}", email); throw new LoginExpiredException(); } } private TokenResponse rotateTokens(String email, MemberRole role) { + log.info("Rotating access and refresh tokens for email: {}", email); Token accessToken = createAccessToken(email, role); Token refreshToken = rotateRefreshToken(email, role); + log.debug("Tokens rotated successfully for email: {}", email); return new TokenResponse(accessToken.getToken(), refreshToken.getToken()); } private Token rotateRefreshToken(String email, MemberRole role) { + log.info("Rotating refresh token for email: {}", email); Map claims = getClaims(email, role); String token = tokenProvider.createToken(claims, tokenProperties.refreshExpireLength()); RefreshToken refreshToken = new RefreshToken(token, email); - tokenRedisClient.saveToken(email, refreshToken.getToken()); + log.debug("New refresh token saved to Redis: {}", refreshToken.getToken()); return refreshToken; } private Map getClaims(String email, MemberRole role) { + log.debug("Creating claims for email: {}, role: {}", email, role); return Map.of( EMAIL_CLAIM, email, ROLE_CLAIM, role.name() @@ -96,47 +112,61 @@ private Map getClaims(String email, MemberRole role) { } public boolean isTokenExpired(String token) { + log.debug("Checking if token is expired: {}", token); return tokenProvider.isTokenExpired(token); } public boolean isTokenSignatureValid(String token) { + log.debug("Checking if token signature is valid: {}", token); try { return tokenProvider.isSignatureValid(token); } catch (IllegalTokenException e) { + log.error("Token signature is invalid: {}", token, e); return false; } } public String extractEmail(String token) { + log.debug("Extracting email from token"); return extractClaim(token, EMAIL_CLAIM); } public String extractMemberRole(String token) { + log.debug("Extracting member role from token"); return extractClaim(token, ROLE_CLAIM); } private String extractClaim(String token, String key) { + log.debug("Extracting claim: {} from token", key); String claim; try { claim = tokenProvider.extractClaim(token, key); } catch (ExpiredJwtException e) { Claims claims = e.getClaims(); + log.warn("Token expired, extracting claim from expired token"); return claims.get(key, String.class); } if (claim == null) { + log.error("Claim {} not found in token", key); throw new IllegalTokenException(); } return claim; } public boolean isNotVerifiedPassword(String rawPassword, String encodedPassword) { + log.debug("Validating password"); return !passwordValidator.matches(rawPassword, encodedPassword); } private void validMemberRefreshToken(String refreshToken, String email) { + log.debug("Validating refresh token for email: {}", email); String foundToken = tokenRedisClient.getToken(email) - .orElseThrow(IllegalTokenException::new); + .orElseThrow(() -> { + log.error("Refresh token not found in Redis for email: {}", email); + return new IllegalTokenException(); + }); if (!foundToken.equals(refreshToken)) { + log.error("Refresh token does not match the one in Redis for email: {}", email); throw new IllegalTokenException(); } } diff --git a/backend/src/main/java/com/cruru/global/AuthenticationInterceptor.java b/backend/src/main/java/com/cruru/global/AuthenticationInterceptor.java index 68629cb9d..14bfd3492 100644 --- a/backend/src/main/java/com/cruru/global/AuthenticationInterceptor.java +++ b/backend/src/main/java/com/cruru/global/AuthenticationInterceptor.java @@ -8,11 +8,13 @@ import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.http.ResponseCookie; import org.springframework.web.servlet.HandlerInterceptor; +@Slf4j @RequiredArgsConstructor public class AuthenticationInterceptor implements HandlerInterceptor { @@ -23,47 +25,65 @@ public class AuthenticationInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { + log.info("Request URI: {}, Method: {}", request.getRequestURI(), request.getMethod()); // 로그 추가 + if (isGetApplyformRequest(request)) { + log.info("GET applyform request is allowed"); return true; } if (isOptionsRequest(request) || isAuthenticated(request)) { + log.info("OPTIONS request or valid authentication"); return true; } if (isValidTokenExpired(request)) { + log.info("Token expired, attempting refresh..."); refresh(request, response); return true; } + log.warn("Unauthorized request: {}", request.getRequestURI()); response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); return false; } private void refresh(HttpServletRequest request, HttpServletResponse response) { String token = cookieManager.extractRefreshToken(request); + log.info("Refresh token extracted: {}", token); + TokenResponse tokenResponse = authService.refresh(token); ResponseCookie accessTokenCookie = cookieManager.createAccessTokenCookie(tokenResponse.accessToken()); ResponseCookie refreshTokenCookie = cookieManager.createRefreshTokenCookie(tokenResponse.refreshToken()); + log.info("New access and refresh tokens created"); + response.addHeader(HttpHeaders.SET_COOKIE, accessTokenCookie.toString()); response.addHeader(HttpHeaders.SET_COOKIE, refreshTokenCookie.toString()); } private boolean isGetApplyformRequest(HttpServletRequest request) { - return request.getRequestURI().matches(APPLYFORM_REQUEST_URI) && isGetRequest(request); + boolean result = request.getRequestURI().matches(APPLYFORM_REQUEST_URI) && isGetRequest(request); + log.info("Is GET applyform request: {}", result); + return result; } private boolean isOptionsRequest(HttpServletRequest request) { - return HttpMethod.OPTIONS.name().equalsIgnoreCase(request.getMethod()); + boolean result = HttpMethod.OPTIONS.name().equalsIgnoreCase(request.getMethod()); + log.info("Is OPTIONS request: {}", result); + return result; } private boolean isAuthenticated(HttpServletRequest request) { try { String token = cookieManager.extractAccessToken(request); - return authService.isTokenSignatureValid(token) && !authService.isTokenExpired(token); + log.info("Access token extracted: {}", token); + boolean valid = authService.isTokenSignatureValid(token) && !authService.isTokenExpired(token); + log.info("Is authenticated: {}", valid); + return valid; } catch (IllegalCookieException e) { + log.error("Illegal cookie exception", e); throw new LoginUnauthorizedException(); } } @@ -71,13 +91,19 @@ private boolean isAuthenticated(HttpServletRequest request) { private boolean isValidTokenExpired(HttpServletRequest request) { try { String token = cookieManager.extractAccessToken(request); - return authService.isTokenSignatureValid(token) && authService.isTokenExpired(token); + log.info("Access token extracted: {}", token); + boolean expired = authService.isTokenSignatureValid(token) && authService.isTokenExpired(token); + log.info("Is token expired: {}", expired); + return expired; } catch (IllegalCookieException e) { + log.error("Illegal cookie exception", e); throw new LoginUnauthorizedException(); } } private boolean isGetRequest(HttpServletRequest request) { - return HttpMethod.GET.name().equalsIgnoreCase(request.getMethod()); + boolean result = HttpMethod.GET.name().equalsIgnoreCase(request.getMethod()); + log.info("Is GET request: {}", result); + return result; } } diff --git a/backend/src/main/java/com/cruru/global/util/CookieManager.java b/backend/src/main/java/com/cruru/global/util/CookieManager.java index 313114c67..ae7bf3c04 100644 --- a/backend/src/main/java/com/cruru/global/util/CookieManager.java +++ b/backend/src/main/java/com/cruru/global/util/CookieManager.java @@ -5,9 +5,11 @@ import jakarta.servlet.http.HttpServletRequest; import java.util.Arrays; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import org.springframework.http.ResponseCookie; import org.springframework.stereotype.Component; +@Slf4j @Component @RequiredArgsConstructor public class CookieManager { @@ -15,42 +17,57 @@ public class CookieManager { private final CookieProperties cookieProperties; public String extractAccessToken(HttpServletRequest request) { + log.info("Extracting access token from cookies"); Cookie[] cookies = extractCookie(request); return Arrays.stream(cookies) .filter(this::isAccessTokenCookie) .findFirst() .map(Cookie::getValue) - .orElseThrow(IllegalCookieException::new); + .orElseThrow(() -> { + log.error("Access token cookie not found or invalid"); + return new IllegalCookieException(); + }); } private Cookie[] extractCookie(HttpServletRequest request) { + log.debug("Extracting cookies from request"); Cookie[] cookies = request.getCookies(); if (cookies == null) { + log.warn("No cookies found in the request"); throw new IllegalCookieException(); } return cookies; } private boolean isAccessTokenCookie(Cookie cookie) { - return cookieProperties.accessTokenKey().equals(cookie.getName()); + boolean result = cookieProperties.accessTokenKey().equals(cookie.getName()); + log.debug("Is access token cookie: {}", result); + return result; } public String extractRefreshToken(HttpServletRequest request) { + log.info("Extracting refresh token from cookies"); Cookie[] cookies = extractCookie(request); return Arrays.stream(cookies) .filter(this::isRefreshTokenCookie) .findFirst() .map(Cookie::getValue) - .orElseThrow(IllegalCookieException::new); + .orElseThrow(() -> { + log.error("Refresh token cookie not found or invalid"); + return new IllegalCookieException(); + }); } private boolean isRefreshTokenCookie(Cookie cookie) { - return cookieProperties.refreshTokenKey().equals(cookie.getName()); + boolean result = cookieProperties.refreshTokenKey().equals(cookie.getName()); + log.debug("Is refresh token cookie: {}", result); + return result; } public ResponseCookie createAccessTokenCookie(String token) { - return ResponseCookie.from(cookieProperties.accessTokenKey(), token) + log.info("Creating access token cookie"); + ResponseCookie cookie = ResponseCookie.from(cookieProperties.accessTokenKey(), token) .httpOnly(cookieProperties.httpOnly()) .secure(cookieProperties.secure()) .domain(cookieProperties.domain()) @@ -58,10 +75,13 @@ public ResponseCookie createAccessTokenCookie(String token) { .sameSite(cookieProperties.sameSite()) .maxAge(cookieProperties.maxAge()) .build(); + log.debug("Access token cookie created: {}", cookie); + return cookie; } public ResponseCookie createRefreshTokenCookie(String refreshToken) { - return ResponseCookie.from(cookieProperties.refreshTokenKey(), refreshToken) + log.info("Creating refresh token cookie"); + ResponseCookie cookie = ResponseCookie.from(cookieProperties.refreshTokenKey(), refreshToken) .httpOnly(cookieProperties.httpOnly()) .secure(cookieProperties.secure()) .domain(cookieProperties.domain()) @@ -69,10 +89,13 @@ public ResponseCookie createRefreshTokenCookie(String refreshToken) { .sameSite(cookieProperties.sameSite()) .maxAge(cookieProperties.maxAge()) .build(); + log.debug("Refresh token cookie created: {}", cookie); + return cookie; } public ResponseCookie clearAccessTokenCookie() { - return ResponseCookie.from(cookieProperties.accessTokenKey()) + log.info("Clearing access token cookie"); + ResponseCookie cookie = ResponseCookie.from(cookieProperties.accessTokenKey()) .httpOnly(cookieProperties.httpOnly()) .secure(cookieProperties.secure()) .domain(cookieProperties.domain()) @@ -80,10 +103,13 @@ public ResponseCookie clearAccessTokenCookie() { .sameSite(cookieProperties.sameSite()) .maxAge(0) .build(); + log.debug("Access token cookie cleared: {}", cookie); + return cookie; } public ResponseCookie clearRefreshTokenCookie() { - return ResponseCookie.from(cookieProperties.refreshTokenKey()) + log.info("Clearing refresh token cookie"); + ResponseCookie cookie = ResponseCookie.from(cookieProperties.refreshTokenKey()) .httpOnly(cookieProperties.httpOnly()) .secure(cookieProperties.secure()) .domain(cookieProperties.domain()) @@ -91,5 +117,7 @@ public ResponseCookie clearRefreshTokenCookie() { .sameSite(cookieProperties.sameSite()) .maxAge(0) .build(); + log.debug("Refresh token cookie cleared: {}", cookie); + return cookie; } }