diff --git a/.github/workflows/CD.yml b/.github/workflows/CD.yml index e28602e..9ec8a01 100644 --- a/.github/workflows/CD.yml +++ b/.github/workflows/CD.yml @@ -138,7 +138,7 @@ jobs: username: ${{ secrets.EC2_USER }} key: ${{ secrets.EC2_KEY }} script: | - sleep 5; + sleep 10; echo "Health Check μ‹œμž‘...🩺" for i in {1..10} @@ -150,7 +150,7 @@ jobs: fi echo "⏳ μ„œλ²„ 기동 λŒ€κΈ°μ€‘... ($i/10)" - sleep 5 + sleep 10 done echo "❌ Health Check μ‹€νŒ¨" diff --git a/src/main/java/org/creditto/authserver/global/aop/LogAspect.java b/src/main/java/org/creditto/authserver/global/aop/LogAspect.java new file mode 100644 index 0000000..773e57c --- /dev/null +++ b/src/main/java/org/creditto/authserver/global/aop/LogAspect.java @@ -0,0 +1,117 @@ +package org.creditto.authserver.global.aop; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.aspectj.lang.JoinPoint; +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.annotation.Around; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Before; +import org.aspectj.lang.annotation.Pointcut; +import org.creditto.authserver.global.response.BaseResponse; +import org.creditto.authserver.global.util.MaskingUtil; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Component; +import org.springframework.validation.BindingResult; +import org.springframework.web.context.request.RequestAttributes; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; + +import java.util.Arrays; + +@Aspect +@Slf4j +@Component +@RequiredArgsConstructor +public class LogAspect { + + private final ObjectMapper objectMapper; + + @Pointcut("execution(* org.creditto.authserver..controller..*(..))") + private void onRequest() { + } + + @Pointcut("execution(* org.creditto.authserver..service..*(..))") + private void onService() { + } + + @Around("onRequest()") + public Object logRequestAndResponse(final ProceedingJoinPoint joinPoint) throws Throwable { + final ServletRequestAttributes requestAttributes = getRequestAttributes(); + final HttpServletRequest request = requestAttributes != null ? requestAttributes.getRequest() : null; + + final String className = joinPoint.getTarget().getClass().getSimpleName(); + final String requestUri = request != null ? request.getRequestURI() : joinPoint.getSignature().toShortString(); + final String method = request != null ? request.getMethod() : "N/A"; + final String requestIp = request != null ? request.getRemoteAddr() : "N/A"; + + log.info("[{}] Request IP : {} | Request URI : {} | Request Method : {}", className, requestIp, requestUri, method); + + final Object[] sanitizedArgs = Arrays.stream(joinPoint.getArgs()) + .filter(arg -> !(arg instanceof HttpServletRequest)) + .filter(arg -> !(arg instanceof HttpServletResponse)) + .filter(arg -> !(arg instanceof BindingResult)) + .toArray(); + + if (sanitizedArgs.length > 0) { + final String argsAsString = MaskingUtil.maskSensitiveData(serialize(sanitizedArgs)); + log.info("[{}] {} {} - RequestBody: {}", className, method, requestUri, argsAsString); + } + + try { + final Object result = joinPoint.proceed(); + Object responseBody = result; + if (result instanceof ResponseEntity responseEntity) { + responseBody = responseEntity.getBody(); + } + final Object dataForLog = responseBody instanceof BaseResponse baseResponse + ? baseResponse.getData() + : responseBody; + final String responseAsString = MaskingUtil.maskSensitiveData(serialize(dataForLog)); + log.info("[{}] {} {} - ResponseData: {}", className, method, requestUri, responseAsString); + return result; + } catch (Throwable e) { + log.error("[{}] {} {} - Exception occurred : {}", className, method, requestUri, e.getMessage()); + throw e; + } + } + + @Before("onService()") + public void beforeServiceLog(final JoinPoint joinPoint) { + final String className = joinPoint.getTarget().getClass().getSimpleName(); + final String methodName = joinPoint.getSignature().getName(); + final Object[] args = joinPoint.getArgs(); + + log.info("[{}] {}() called", className, methodName); + + if (args.length > 0) { + final String params = MaskingUtil.maskSensitiveData(serialize(args)); + log.debug("[{}] Parameters: {}", className, params); + } + } + + private ServletRequestAttributes getRequestAttributes() { + final RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes(); + if (requestAttributes instanceof ServletRequestAttributes servletRequestAttributes) { + return servletRequestAttributes; + } + return null; + } + + private String serialize(final Object value) { + if (value == null) { + return "null"; + } + + try { + return objectMapper.writeValueAsString(value); + } catch (JsonProcessingException e) { + log.debug("직렬화 μ‹€νŒ¨: {}", e.getMessage()); + return String.valueOf(value); + } + } +} diff --git a/src/main/java/org/creditto/authserver/global/util/MaskingUtil.java b/src/main/java/org/creditto/authserver/global/util/MaskingUtil.java new file mode 100644 index 0000000..2bc2865 --- /dev/null +++ b/src/main/java/org/creditto/authserver/global/util/MaskingUtil.java @@ -0,0 +1,96 @@ +package org.creditto.authserver.global.util; + +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public final class MaskingUtil { + + private static final List SENSITIVE_KEYS = List.of( + "password", + "secret", + "token", + "key", + "certificateNumber" + ); + + private static final Pattern JSON_KEY_VALUE_PATTERN = Pattern.compile( + "(\"(?:" + String.join("|", SENSITIVE_KEYS) + ")\"\\s*:\\s*\")(.*?)(\")", + Pattern.CASE_INSENSITIVE + ); + + private static final Pattern KEY_VALUE_PATTERN = Pattern.compile( + "(?i)((?:password|secret|token|key[number]?|certificateNumber)\\s*[=:]\\s*)([^,\\s}\\]]+)" + ); + + private static final Pattern PHONE_PATTERN = Pattern.compile("(?