Skip to content

Commit

Permalink
feat: log 설정 (#925)
Browse files Browse the repository at this point in the history
  • Loading branch information
Chocochip101 authored Oct 24, 2024
1 parent 2d01253 commit 215bdf0
Show file tree
Hide file tree
Showing 3 changed files with 100 additions and 16 deletions.
36 changes: 33 additions & 3 deletions backend/src/main/java/com/cruru/auth/service/AuthService.java
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -32,111 +33,140 @@ 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<String, Object> 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<String, Object> 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<String, Object> 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<String, Object> getClaims(String email, MemberRole role) {
log.debug("Creating claims for email: {}, role: {}", email, role);
return Map.of(
EMAIL_CLAIM, email,
ROLE_CLAIM, role.name()
);
}

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();
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 {

Expand All @@ -23,61 +25,85 @@ 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();
}
}

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;
}
}
44 changes: 36 additions & 8 deletions backend/src/main/java/com/cruru/global/util/CookieManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,91 +5,119 @@
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 {

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())
.path(cookieProperties.path())
.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())
.path(cookieProperties.path())
.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())
.path(cookieProperties.path())
.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())
.path(cookieProperties.path())
.sameSite(cookieProperties.sameSite())
.maxAge(0)
.build();
log.debug("Refresh token cookie cleared: {}", cookie);
return cookie;
}
}

0 comments on commit 215bdf0

Please sign in to comment.