diff --git a/src/main/java/com/eactive/apim/portal/common/util/HttpRequestUtil.java b/src/main/java/com/eactive/apim/portal/common/util/HttpRequestUtil.java new file mode 100644 index 0000000..db8b518 --- /dev/null +++ b/src/main/java/com/eactive/apim/portal/common/util/HttpRequestUtil.java @@ -0,0 +1,99 @@ +package com.eactive.apim.portal.common.util; + +import javax.servlet.http.HttpServletRequest; + +/** + * HTTP 요청 관련 유틸리티 클래스 + */ +public class HttpRequestUtil { + + private static final String[] IP_HEADER_CANDIDATES = { + "X-Forwarded-For", + "Proxy-Client-IP", + "WL-Proxy-Client-IP", + "HTTP_X_FORWARDED_FOR", + "HTTP_X_FORWARDED", + "HTTP_X_CLUSTER_CLIENT_IP", + "HTTP_CLIENT_IP", + "HTTP_FORWARDED_FOR", + "HTTP_FORWARDED", + "HTTP_VIA", + "REMOTE_ADDR", + "X-Real-IP" + }; + + /** + * Proxy를 통한 접속도 고려하여 실제 클라이언트 IP 주소를 반환 + * + * @param request HttpServletRequest 객체 + * @return 클라이언트 IP 주소 + */ + public static String getClientIpAddress(HttpServletRequest request) { + if (request == null) { + return "unknown"; + } + + String ip = null; + + // 헤더들을 순회하면서 IP 주소 찾기 + for (String header : IP_HEADER_CANDIDATES) { + ip = request.getHeader(header); + if (ip != null && !ip.isEmpty() && !"unknown".equalsIgnoreCase(ip)) { + break; + } + } + + // 헤더에서 IP를 찾지 못한 경우 기본 메서드 사용 + if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) { + ip = request.getRemoteAddr(); + } + + // X-Forwarded-For 헤더는 여러 IP를 포함할 수 있음 (쉼표로 구분) + // 첫 번째 IP가 실제 클라이언트 IP + if (ip != null && ip.contains(",")) { + ip = ip.split(",")[0].trim(); + } + + return ip; + } + + /** + * Proxy를 통한 접속도 고려하여 실제 클라이언트 호스트명을 반환 + * + * @param request HttpServletRequest 객체 + * @return 클라이언트 호스트명 + */ + public static String getClientHost(HttpServletRequest request) { + if (request == null) { + return "unknown"; + } + + String host = request.getHeader("X-Forwarded-Host"); + if (host == null || host.isEmpty() || "unknown".equalsIgnoreCase(host)) { + host = request.getRemoteHost(); + } + + return host; + } + + /** + * 요청이 Proxy를 통해 들어왔는지 확인 + * + * @param request HttpServletRequest 객체 + * @return Proxy를 통한 접속이면 true, 아니면 false + */ + public static boolean isProxied(HttpServletRequest request) { + if (request == null) { + return false; + } + + for (String header : IP_HEADER_CANDIDATES) { + String value = request.getHeader(header); + if (value != null && !value.isEmpty() && !"unknown".equalsIgnoreCase(value)) { + return true; + } + } + + return false; + } +} diff --git a/src/main/java/com/eactive/apim/portal/common/util/StringRepeatUtil.java b/src/main/java/com/eactive/apim/portal/common/util/StringRepeatUtil.java new file mode 100644 index 0000000..b1259d3 --- /dev/null +++ b/src/main/java/com/eactive/apim/portal/common/util/StringRepeatUtil.java @@ -0,0 +1,43 @@ +package com.eactive.apim.portal.common.util; + +/** + * JDK 8 호환성을 위한 문자열 반복 유틸리티 + */ +public class StringRepeatUtil { + + /** + * 문자를 지정된 횟수만큼 반복하여 문자열을 생성 + * + * @param ch 반복할 문자 + * @param count 반복 횟수 + * @return 반복된 문자열 + */ + public static String repeat(char ch, int count) { + if (count <= 0) { + return ""; + } + char[] chars = new char[count]; + for (int i = 0; i < count; i++) { + chars[i] = ch; + } + return new String(chars); + } + + /** + * 문자열을 지정된 횟수만큼 반복 + * + * @param str 반복할 문자열 + * @param count 반복 횟수 + * @return 반복된 문자열 + */ + public static String repeat(String str, int count) { + if (str == null || count <= 0) { + return ""; + } + StringBuilder sb = new StringBuilder(str.length() * count); + for (int i = 0; i < count; i++) { + sb.append(str); + } + return sb.toString(); + } +} diff --git a/src/main/java/com/eactive/apim/portal/config/PortalAuthenticationFailureHandler.java b/src/main/java/com/eactive/apim/portal/config/PortalAuthenticationFailureHandler.java index 8b9b684..db2d66f 100644 --- a/src/main/java/com/eactive/apim/portal/config/PortalAuthenticationFailureHandler.java +++ b/src/main/java/com/eactive/apim/portal/config/PortalAuthenticationFailureHandler.java @@ -3,6 +3,8 @@ package com.eactive.apim.portal.config; import com.eactive.apim.portal.apps.login.constants.LoginConstants; import com.eactive.apim.portal.apps.user.service.PortalUserLogService; import com.eactive.apim.portal.common.exception.UserNotFoundException; +import com.eactive.apim.portal.common.util.HttpRequestUtil; +import com.eactive.apim.portal.common.util.StringRepeatUtil; import com.eactive.apim.portal.portaluser.entity.PortalUser; import com.eactive.apim.portal.portaluser.repository.PortalUserRepository; import com.eactive.apim.portal.template.entity.MessageCode; @@ -23,6 +25,8 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import java.io.IOException; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; /** * Created by Sungpil Hyun @@ -32,6 +36,9 @@ import java.io.IOException; public class PortalAuthenticationFailureHandler implements AuthenticationFailureHandler { private static final Logger logger = LoggerFactory.getLogger(PortalAuthenticationFailureHandler.class); + private static final Logger sessionLogger = LoggerFactory.getLogger("eapim.portal.session"); + private static final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS"); + private final PortalUserRepository portalUserRepository; private final PortalUserLogService userLogService; private final MessageHandlerService messageHandlerService; @@ -82,10 +89,39 @@ public class PortalAuthenticationFailureHandler implements AuthenticationFailure userLogService.logFailure(username, ip, sessionId); + // 로그인 실패 시 세션 정보 로깅 + logLoginFailure(request, username, exception); + HttpSession session = request.getSession(); session.setAttribute(LoginConstants.LOGIN_MESSAGE, exception.getLocalizedMessage()); session.setAttribute("loginId", username); String contextPath = request.getContextPath(); response.sendRedirect(contextPath + "/login"); } + + private void logLoginFailure(HttpServletRequest request, String username, AuthenticationException exception) { + StringBuilder logMessage = new StringBuilder(); + logMessage.append("\n"); + logMessage.append(StringRepeatUtil.repeat('=', 80)).append("\n"); + logMessage.append("USER LOGIN FAILURE\n"); + logMessage.append(StringRepeatUtil.repeat('=', 80)).append("\n"); + logMessage.append("Username: ").append(username).append("\n"); + logMessage.append("Session ID: ").append(request.getSession().getId()).append("\n"); + logMessage.append("Failed At: ").append(LocalDateTime.now().format(formatter)).append("\n"); + logMessage.append("Failure Reason: ").append(exception.getLocalizedMessage()).append("\n"); + logMessage.append("\n"); + logMessage.append(StringRepeatUtil.repeat('-', 80)).append("\n"); + logMessage.append("REQUEST INFORMATION\n"); + logMessage.append(StringRepeatUtil.repeat('-', 80)).append("\n"); + logMessage.append("Client IP Address: ").append(HttpRequestUtil.getClientIpAddress(request)).append("\n"); + logMessage.append("Client Host: ").append(HttpRequestUtil.getClientHost(request)).append("\n"); + logMessage.append("Is Proxied: ").append(HttpRequestUtil.isProxied(request)).append("\n"); + logMessage.append("Remote Address (Direct): ").append(request.getRemoteAddr()).append("\n"); + logMessage.append("Remote Host (Direct): ").append(request.getRemoteHost()).append("\n"); + logMessage.append("Request URI: ").append(request.getRequestURI()).append("\n"); + logMessage.append("User-Agent: ").append(request.getHeader("User-Agent")).append("\n"); + logMessage.append(StringRepeatUtil.repeat('=', 80)).append("\n"); + + sessionLogger.warn(logMessage.toString()); + } } diff --git a/src/main/java/com/eactive/apim/portal/config/PortalAuthenticationSuccessHandler.java b/src/main/java/com/eactive/apim/portal/config/PortalAuthenticationSuccessHandler.java index 0510d8c..15c939d 100644 --- a/src/main/java/com/eactive/apim/portal/config/PortalAuthenticationSuccessHandler.java +++ b/src/main/java/com/eactive/apim/portal/config/PortalAuthenticationSuccessHandler.java @@ -2,6 +2,8 @@ package com.eactive.apim.portal.config; import com.eactive.apim.portal.apps.user.repository.PortalOrgRepository; import com.eactive.apim.portal.apps.user.service.PortalUserLogService; +import com.eactive.apim.portal.common.util.HttpRequestUtil; +import com.eactive.apim.portal.common.util.StringRepeatUtil; import com.eactive.apim.portal.invitation.entity.UserInvitation; import com.eactive.apim.portal.invitation.entity.UserInvitationEnums.InvitationStatus; import com.eactive.apim.portal.invitation.repository.UserInvitationRepository; @@ -15,6 +17,8 @@ import com.eactive.apim.portal.template.entity.MessageCode; import com.eactive.apim.portal.template.entity.MessageRequest; import com.eactive.apim.portal.template.repository.MessageRequestRepository; import lombok.RequiredArgsConstructor; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.security.core.Authentication; import org.springframework.security.web.authentication.AuthenticationSuccessHandler; import org.springframework.stereotype.Service; @@ -26,6 +30,7 @@ import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import java.io.IOException; import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; import java.util.Optional; @@ -34,6 +39,9 @@ import java.util.Optional; @RequiredArgsConstructor public class PortalAuthenticationSuccessHandler implements AuthenticationSuccessHandler { + private static final Logger sessionLogger = LoggerFactory.getLogger("eapim.portal.session"); + private static final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS"); + private final PortalUserRepository portalUserRepository; private final PortalProperties portalProperties; private final PortalUserLogService userLogService; @@ -104,6 +112,9 @@ public class PortalAuthenticationSuccessHandler implements AuthenticationSuccess } } + // 로그인 성공 시 세션 정보 로깅 + logLoginSuccess(request, session, username); + String decisionToken = (String) request.getSession().getAttribute("decisionToken"); if (decisionToken != null) { response.sendRedirect(contextPath + "/signup/decision_process"); @@ -112,6 +123,31 @@ public class PortalAuthenticationSuccessHandler implements AuthenticationSuccess } } + private void logLoginSuccess(HttpServletRequest request, HttpSession session, String username) { + StringBuilder logMessage = new StringBuilder(); + logMessage.append("\n"); + logMessage.append(StringRepeatUtil.repeat('=', 80)).append("\n"); + logMessage.append("USER LOGIN SUCCESS\n"); + logMessage.append(StringRepeatUtil.repeat('=', 80)).append("\n"); + logMessage.append("Username: ").append(username).append("\n"); + logMessage.append("Session ID: ").append(session.getId()).append("\n"); + logMessage.append("Login At: ").append(LocalDateTime.now().format(formatter)).append("\n"); + logMessage.append("\n"); + logMessage.append(StringRepeatUtil.repeat('-', 80)).append("\n"); + logMessage.append("REQUEST INFORMATION\n"); + logMessage.append(StringRepeatUtil.repeat('-', 80)).append("\n"); + logMessage.append("Client IP Address: ").append(HttpRequestUtil.getClientIpAddress(request)).append("\n"); + logMessage.append("Client Host: ").append(HttpRequestUtil.getClientHost(request)).append("\n"); + logMessage.append("Is Proxied: ").append(HttpRequestUtil.isProxied(request)).append("\n"); + logMessage.append("Remote Address (Direct): ").append(request.getRemoteAddr()).append("\n"); + logMessage.append("Remote Host (Direct): ").append(request.getRemoteHost()).append("\n"); + logMessage.append("Request URI: ").append(request.getRequestURI()).append("\n"); + logMessage.append("User-Agent: ").append(request.getHeader("User-Agent")).append("\n"); + logMessage.append(StringRepeatUtil.repeat('=', 80)).append("\n"); + + sessionLogger.info(logMessage.toString()); + } + private boolean isPasswordChangeRequired(PortalUser user) { // 가장 최근 비밀번호 변경 이력 조회 Optional latestHistory = passwordHistoryRepository diff --git a/src/main/java/com/eactive/apim/portal/config/PortalConfigLog.java b/src/main/java/com/eactive/apim/portal/config/PortalConfigLog.java index dc3b892..f79ec29 100644 --- a/src/main/java/com/eactive/apim/portal/config/PortalConfigLog.java +++ b/src/main/java/com/eactive/apim/portal/config/PortalConfigLog.java @@ -1,5 +1,6 @@ package com.eactive.apim.portal.config; +import com.eactive.apim.portal.common.util.HttpRequestUtil; import com.eactive.apim.portal.common.util.SecurityUtil; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.Aspect; @@ -29,7 +30,7 @@ public class PortalConfigLog { String method = request.getMethod(); String path = request.getRequestURI(); String query = request.getQueryString(); - String ip = request.getRemoteAddr(); + String ip = HttpRequestUtil.getClientIpAddress(request); String user = SecurityUtil.getCurrentLoginId(); diff --git a/src/main/java/com/eactive/apim/portal/config/PortalConfigSecurity.java b/src/main/java/com/eactive/apim/portal/config/PortalConfigSecurity.java index 0191d6f..43bc85b 100644 --- a/src/main/java/com/eactive/apim/portal/config/PortalConfigSecurity.java +++ b/src/main/java/com/eactive/apim/portal/config/PortalConfigSecurity.java @@ -31,13 +31,17 @@ public class PortalConfigSecurity { private final PortalAuthenticationManager portalAuthenticationManager; + private final PortalLogoutSuccessHandler logoutSuccessHandler; + @Autowired public PortalConfigSecurity(PortalAuthenticationFailureHandler authenticationFailureHandler, PortalAuthenticationSuccessHandler authenticationSuccessHandler, - PortalAuthenticationManager portalAuthenticationManager) { + PortalAuthenticationManager portalAuthenticationManager, + PortalLogoutSuccessHandler logoutSuccessHandler) { this.authenticationFailureHandler = authenticationFailureHandler; this.authenticationSuccessHandler = authenticationSuccessHandler; this.portalAuthenticationManager = portalAuthenticationManager; + this.logoutSuccessHandler = logoutSuccessHandler; } @Bean @@ -71,7 +75,7 @@ public class PortalConfigSecurity { .successHandler(authenticationSuccessHandler)) .logout(logout -> logout .logoutRequestMatcher(new AntPathRequestMatcher("/actionLogout.do", "GET")) - .logoutSuccessUrl("/")) + .logoutSuccessHandler(logoutSuccessHandler)) .csrf(csrf -> csrf .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()) .ignoringRequestMatchers(new AntPathRequestMatcher("/_proxy/**/*")) diff --git a/src/main/java/com/eactive/apim/portal/config/PortalLogoutSuccessHandler.java b/src/main/java/com/eactive/apim/portal/config/PortalLogoutSuccessHandler.java new file mode 100644 index 0000000..6e955db --- /dev/null +++ b/src/main/java/com/eactive/apim/portal/config/PortalLogoutSuccessHandler.java @@ -0,0 +1,87 @@ +package com.eactive.apim.portal.config; + +import com.eactive.apim.portal.common.util.HttpRequestUtil; +import com.eactive.apim.portal.common.util.StringRepeatUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.security.core.Authentication; +import org.springframework.security.web.authentication.logout.LogoutSuccessHandler; +import org.springframework.stereotype.Component; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; +import java.io.IOException; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.Enumeration; + +/** + * 로그아웃 성공 시 세션 정보를 로깅하는 핸들러 + */ +@Component +public class PortalLogoutSuccessHandler implements LogoutSuccessHandler { + + private static final Logger sessionLogger = LoggerFactory.getLogger("eapim.portal.session"); + private static final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS"); + + @Override + public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, + Authentication authentication) throws IOException, ServletException { + HttpSession session = request.getSession(false); + + if (session != null) { + StringBuilder logMessage = new StringBuilder(); + logMessage.append("\n"); + logMessage.append(StringRepeatUtil.repeat('=', 80)).append("\n"); + logMessage.append("USER LOGOUT\n"); + logMessage.append(StringRepeatUtil.repeat('=', 80)).append("\n"); + logMessage.append("Session ID: ").append(session.getId()).append("\n"); + logMessage.append("Logout At: ").append(LocalDateTime.now().format(formatter)).append("\n"); + + if (authentication != null) { + logMessage.append("Username: ").append(authentication.getName()).append("\n"); + logMessage.append("Authenticated: ").append(authentication.isAuthenticated()).append("\n"); + } + + logMessage.append("\n"); + logMessage.append(StringRepeatUtil.repeat('-', 80)).append("\n"); + logMessage.append("REQUEST INFORMATION\n"); + logMessage.append(StringRepeatUtil.repeat('-', 80)).append("\n"); + logMessage.append("Client IP Address: ").append(HttpRequestUtil.getClientIpAddress(request)).append("\n"); + logMessage.append("Client Host: ").append(HttpRequestUtil.getClientHost(request)).append("\n"); + logMessage.append("Is Proxied: ").append(HttpRequestUtil.isProxied(request)).append("\n"); + logMessage.append("Remote Address (Direct): ").append(request.getRemoteAddr()).append("\n"); + logMessage.append("Remote Host (Direct): ").append(request.getRemoteHost()).append("\n"); + logMessage.append("Request URI: ").append(request.getRequestURI()).append("\n"); + logMessage.append("User-Agent: ").append(request.getHeader("User-Agent")).append("\n"); + + logMessage.append("\n"); + logMessage.append(StringRepeatUtil.repeat('-', 80)).append("\n"); + logMessage.append("SESSION ATTRIBUTES (Before Invalidation)\n"); + logMessage.append(StringRepeatUtil.repeat('-', 80)).append("\n"); + + try { + Enumeration attributeNames = session.getAttributeNames(); + while (attributeNames.hasMoreElements()) { + String attrName = attributeNames.nextElement(); + Object attrValue = session.getAttribute(attrName); + String valueStr = attrValue != null ? attrValue.toString() : "null"; + if (valueStr.length() > 100) { + valueStr = valueStr.substring(0, 97) + "..."; + } + logMessage.append(String.format(" %-30s : %s\n", attrName, valueStr)); + } + } catch (Exception e) { + logMessage.append(" Unable to retrieve session attributes\n"); + } + + logMessage.append(StringRepeatUtil.repeat('=', 80)).append("\n"); + + sessionLogger.info(logMessage.toString()); + } + + response.sendRedirect(request.getContextPath() + "/"); + } +} diff --git a/src/main/java/com/eactive/apim/portal/config/SessionLoggingListener.java b/src/main/java/com/eactive/apim/portal/config/SessionLoggingListener.java new file mode 100644 index 0000000..91154df --- /dev/null +++ b/src/main/java/com/eactive/apim/portal/config/SessionLoggingListener.java @@ -0,0 +1,134 @@ +package com.eactive.apim.portal.config; + +import com.eactive.apim.portal.common.util.HttpRequestUtil; +import com.eactive.apim.portal.common.util.StringRepeatUtil; +import lombok.extern.slf4j.Slf4j; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Component; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpSession; +import javax.servlet.http.HttpSessionEvent; +import javax.servlet.http.HttpSessionListener; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.Enumeration; + +/** + * HttpSession 생성 시 요청 헤더 정보를 로깅하는 리스너 + */ +@Component +public class SessionLoggingListener implements HttpSessionListener { + + private static final Logger sessionLogger = LoggerFactory.getLogger("eapim.portal.session"); + private static final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS"); + + @Override + public void sessionCreated(HttpSessionEvent se) { + HttpSession session = se.getSession(); + + // HttpServletRequest 가져오기 + HttpServletRequest request = (HttpServletRequest) session.getAttribute("org.springframework.web.context.request.RequestContextListener.REQUEST_ATTRIBUTES"); + + // Spring의 RequestContextHolder를 통해 현재 요청 가져오기 + try { + org.springframework.web.context.request.RequestAttributes requestAttributes = + org.springframework.web.context.request.RequestContextHolder.getRequestAttributes(); + + if (requestAttributes instanceof org.springframework.web.context.request.ServletRequestAttributes) { + request = ((org.springframework.web.context.request.ServletRequestAttributes) requestAttributes).getRequest(); + } + } catch (Exception e) { + // RequestContextHolder를 통해 요청을 가져올 수 없는 경우 무시 + } + + StringBuilder logMessage = new StringBuilder(); + logMessage.append("\n"); + logMessage.append(StringRepeatUtil.repeat('=', 80)).append("\n"); + logMessage.append("NEW HTTP SESSION CREATED\n"); + logMessage.append(StringRepeatUtil.repeat('=', 80)).append("\n"); + logMessage.append("Session ID: ").append(session.getId()).append("\n"); + logMessage.append("Created At: ").append(LocalDateTime.now().format(formatter)).append("\n"); + logMessage.append("Max Inactive Interval: ").append(session.getMaxInactiveInterval()).append(" seconds\n"); + + if (request != null) { + logMessage.append("\n"); + logMessage.append(StringRepeatUtil.repeat('-', 80)).append("\n"); + logMessage.append("REQUEST INFORMATION\n"); + logMessage.append(StringRepeatUtil.repeat('-', 80)).append("\n"); + logMessage.append("Client IP Address: ").append(HttpRequestUtil.getClientIpAddress(request)).append("\n"); + logMessage.append("Client Host: ").append(HttpRequestUtil.getClientHost(request)).append("\n"); + logMessage.append("Is Proxied: ").append(HttpRequestUtil.isProxied(request)).append("\n"); + logMessage.append("Remote Address (Direct): ").append(request.getRemoteAddr()).append("\n"); + logMessage.append("Remote Host (Direct): ").append(request.getRemoteHost()).append("\n"); + logMessage.append("Remote Port: ").append(request.getRemotePort()).append("\n"); + logMessage.append("Request Method: ").append(request.getMethod()).append("\n"); + logMessage.append("Request URI: ").append(request.getRequestURI()).append("\n"); + logMessage.append("Request URL: ").append(request.getRequestURL()).append("\n"); + logMessage.append("Query String: ").append(request.getQueryString()).append("\n"); + logMessage.append("Protocol: ").append(request.getProtocol()).append("\n"); + logMessage.append("Scheme: ").append(request.getScheme()).append("\n"); + logMessage.append("Server Name: ").append(request.getServerName()).append("\n"); + logMessage.append("Server Port: ").append(request.getServerPort()).append("\n"); + logMessage.append("Context Path: ").append(request.getContextPath()).append("\n"); + + logMessage.append("\n"); + logMessage.append(StringRepeatUtil.repeat('-', 80)).append("\n"); + logMessage.append("REQUEST HEADERS\n"); + logMessage.append(StringRepeatUtil.repeat('-', 80)).append("\n"); + + Enumeration headerNames = request.getHeaderNames(); + while (headerNames.hasMoreElements()) { + String headerName = headerNames.nextElement(); + Enumeration headerValues = request.getHeaders(headerName); + while (headerValues.hasMoreElements()) { + String headerValue = headerValues.nextElement(); + logMessage.append(String.format(" %-30s : %s\n", headerName, headerValue)); + } + } + } else { + logMessage.append("\n"); + logMessage.append("WARNING: HttpServletRequest not available\n"); + } + + logMessage.append(StringRepeatUtil.repeat('=', 80)).append("\n"); + + sessionLogger.info(logMessage.toString()); + } + + @Override + public void sessionDestroyed(HttpSessionEvent se) { + HttpSession session = se.getSession(); + + StringBuilder logMessage = new StringBuilder(); + logMessage.append("\n"); + logMessage.append(StringRepeatUtil.repeat('=', 80)).append("\n"); + logMessage.append("HTTP SESSION DESTROYED\n"); + logMessage.append(StringRepeatUtil.repeat('=', 80)).append("\n"); + logMessage.append("Session ID: ").append(session.getId()).append("\n"); + logMessage.append("Destroyed At: ").append(LocalDateTime.now().format(formatter)).append("\n"); + logMessage.append("Max Inactive Interval: ").append(session.getMaxInactiveInterval()).append(" seconds\n"); + + // 세션 속성 정보 로깅 + try { + logMessage.append("\n"); + logMessage.append(StringRepeatUtil.repeat('-', 80)).append("\n"); + logMessage.append("SESSION ATTRIBUTES\n"); + logMessage.append(StringRepeatUtil.repeat('-', 80)).append("\n"); + + java.util.Enumeration attributeNames = session.getAttributeNames(); + while (attributeNames.hasMoreElements()) { + String attrName = attributeNames.nextElement(); + Object attrValue = session.getAttribute(attrName); + logMessage.append(String.format(" %-30s : %s\n", attrName, attrValue)); + } + } catch (Exception e) { + logMessage.append(" Unable to retrieve session attributes\n"); + } + + logMessage.append(StringRepeatUtil.repeat('=', 80)).append("\n"); + + sessionLogger.info(logMessage.toString()); + } +} diff --git a/src/main/resources/logback-debug.xml b/src/main/resources/logback-debug.xml index 3892104..db0b928 100644 --- a/src/main/resources/logback-debug.xml +++ b/src/main/resources/logback-debug.xml @@ -46,6 +46,17 @@ + + ${LOG_PATH}/http-session.log + + ${LOG_PATH}/backup/http-session.%d{yyyy-MM-dd}.log + 30 + + + %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level - %msg%n + + + @@ -79,4 +90,8 @@ + + + + \ No newline at end of file diff --git a/src/main/resources/logback-gf63.xml b/src/main/resources/logback-gf63.xml index 27f5050..849fd81 100644 --- a/src/main/resources/logback-gf63.xml +++ b/src/main/resources/logback-gf63.xml @@ -30,6 +30,17 @@ + + ${LOG_PATH}/http-session.log + + ${LOG_PATH}/backup/http-session.%d{yyyy-MM-dd}.log + 30 + + + %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level - %msg%n + + + @@ -65,4 +76,8 @@ + + + + \ No newline at end of file diff --git a/src/main/resources/logback-rinjae.xml b/src/main/resources/logback-rinjae.xml index 59120c4..ac69065 100644 --- a/src/main/resources/logback-rinjae.xml +++ b/src/main/resources/logback-rinjae.xml @@ -38,6 +38,17 @@ + + ${LOG_PATH}/http-session.log + + ${LOG_PATH}/backup/http-session.%d{yyyy-MM-dd}.log + 30 + + + %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level - %msg%n + + + @@ -79,4 +90,8 @@ + + + + \ No newline at end of file diff --git a/src/main/resources/logback-spring.xml b/src/main/resources/logback-spring.xml index 97170c0..3ea3db8 100644 --- a/src/main/resources/logback-spring.xml +++ b/src/main/resources/logback-spring.xml @@ -46,6 +46,17 @@ + + ${LOG_PATH}/http-session.log + + ${LOG_PATH}/backup/http-session.%d{yyyy-MM-dd}.log + 30 + + + %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level - %msg%n + + + ${CONSOLE_PATTERN} @@ -55,6 +66,10 @@ + + + +