eunja511005 / AutoCoding

0 stars 0 forks source link

로그 추출 위치를 컨트롤러에서 필터로 변경 #82

Open ywbestPark opened 1 year ago

ywbestPark commented 1 year ago

import org.springframework.util.StringUtils; import org.springframework.web.util.ContentCachingRequestWrapper; import org.springframework.web.util.ContentCachingResponseWrapper;

import javax.servlet.*; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.HashMap; import java.util.Map;

public class CustomFilter implements Filter {

@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
    ContentCachingRequestWrapper requestWrapper = new ContentCachingRequestWrapper((HttpServletRequest) servletRequest);
    ContentCachingResponseWrapper responseWrapper = new ContentCachingResponseWrapper((HttpServletResponse) servletResponse);

    filterChain.doFilter(requestWrapper, responseWrapper);

    String requestContent = extractRequestData(requestWrapper);
    String responseContent = extractResponseData(responseWrapper);

    // 추출한 데이터를 데이터베이스에 저장하는 로직을 구현합니다.
    saveDataToDatabase(requestContent, responseContent);
}

private String extractRequestData(ContentCachingRequestWrapper requestWrapper) {
    StringBuilder requestData = new StringBuilder();

    // 요청 URL과 메서드 추출
    String url = requestWrapper.getRequestURL().toString();
    String method = requestWrapper.getMethod();

    requestData.append("Request URL: ").append(url).append(System.lineSeparator());
    requestData.append("Request Method: ").append(method).append(System.lineSeparator());

    // 요청 헤더 추출
    Map<String, String> headers = new HashMap<>();
    requestWrapper.getHeaderNames().asIterator()
            .forEachRemaining(headerName -> headers.put(headerName, requestWrapper.getHeader(headerName)));

    if (!headers.isEmpty()) {
        requestData.append("Request Headers: ").append(System.lineSeparator());
        headers.forEach((headerName, headerValue) -> requestData.append(headerName).append(": ").append(headerValue).append(System.lineSeparator()));
    }

    // 요청 파라미터 추출
    Map<String, String[]> parameterMap = requestWrapper.getParameterMap();

    if (!parameterMap.isEmpty()) {
        requestData.append("Request Parameters: ").append(System.lineSeparator());
        parameterMap.forEach((paramName, paramValues) -> {
            String paramValue = Arrays.toString(paramValues);
            requestData.append(paramName).append(": ").append(paramValue).append(System.lineSeparator());
        });
    }

    // 요청 바디 추출
    byte[] requestBody = requestWrapper.getContentAsByteArray();

    if (requestBody.length > 0) {
        String requestBodyString = new String(requestBody, StandardCharsets.UTF_8);
        requestData.append("Request Body: ").append(requestBodyString);
    }

    return requestData.toString();
}

private String extractResponseData(ContentCachingResponseWrapper responseWrapper) {
    StringBuilder responseData = new StringBuilder();

    // 응답 상태 코드 추출
    int statusCode = responseWrapper.getStatus();
    responseData.append("Response Status Code: ").append(statusCode).append(System.lineSeparator());

    // 응답 헤더 추출
    Map<String, String> headers = new HashMap<>();
    responseWrapper.getHeaderNames().forEach(headerName -> headers.put(headerName, responseWrapper.getHeader(headerName)));

    if (!headers.isEmpty()) {
        responseData.append("Response Headers: ").append(System.lineSeparator());
        headers.forEach((headerName, headerValue) -> responseData.append(headerName).append(": ").append(headerValue).append(System.lineSeparator()));
    }

    // 응답 바디 추출
    byte[] responseBody = responseWrapper.getContentAsByteArray();

    if (responseBody.length > 0) {
        String responseBodyString = new String(responseBody, StandardCharsets.UTF_8);
        responseData.append("Response Body: ").append(responseBodyString);
    }

    return responseData.toString();
}

private void saveDataToDatabase(String requestContent, String responseContent) {
    // 데이터베이스에 요청 및 응답 데이터를 저장하는 로직을 구현합니다.
    // 예: JPA를 사용하여 엔티티에 데이터를 매핑하고 저장합니다.
}

// init() 및 destroy() 메서드는 필요에 따라 구현할 수 있습니다.

}

ywbestPark commented 1 year ago

import org.springframework.web.util.ContentCachingRequestWrapper; import org.springframework.web.util.ContentCachingResponseWrapper;

import javax.servlet.*; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.nio.charset.StandardCharsets;

public class CustomFilter implements Filter {

@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
    ContentCachingRequestWrapper requestWrapper = new ContentCachingRequestWrapper((HttpServletRequest) servletRequest);
    ContentCachingResponseWrapper responseWrapper = new ContentCachingResponseWrapper((HttpServletResponse) servletResponse);

    filterChain.doFilter(requestWrapper, responseWrapper);

    String requestContent = extractRequestData(requestWrapper);
    String responseContent = extractResponseData(responseWrapper);

    // 추출한 데이터를 데이터베이스에 저장하는 로직을 구현합니다.
    saveDataToDatabase(requestContent, responseContent);
}

private String extractRequestData(ContentCachingRequestWrapper requestWrapper) {
    String requestData = "";

    if (requestWrapper.getContentAsByteArray().length > 0) {
        requestData = new String(requestWrapper.getContentAsByteArray(), StandardCharsets.UTF_8);
    }

    // 요청 데이터 추출 로직을 구현합니다.
    // 예: 파라미터, 폼 데이터, 바디 데이터, 패스 변수 등을 추출합니다.

    return requestData;
}

private String extractResponseData(ContentCachingResponseWrapper responseWrapper) {
    String responseData = "";

    if (responseWrapper.getContentAsByteArray().length > 0) {
        responseData = new String(responseWrapper.getContentAsByteArray(), StandardCharsets.UTF_8);
    }

    // 응답 데이터 추출 로직을 구현합니다.
    // 예: 바디 데이터, 헤더 등을 추출합니다.

    return responseData;
}

private void saveDataToDatabase(String requestContent, String responseContent) {
    // 데이터베이스에 요청 및 응답 데이터를 저장하는 로직을 구현합니다.
    // 예: JPA를 사용하여 엔티티에 데이터를 매핑하고 저장합니다.
}

// init() 및 destroy() 메서드는 필요에 따라 구현할 수 있습니다.

}

eunja511005 commented 1 year ago

[중요]

필터 추가시 아래 소스 안 넣으면 로컬에서는 오류 안나지만 리눅스 서버에서는 서버 시작도 안됨

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        // Do nothing
    }

    @Override
    public void destroy() {
        // Do nothing
    }
eunja511005 commented 1 year ago

[이슈] Filter에서 getInputStream()을 한번 호출해 버리면 @RequestBody DTO 사용시 SpringMVC에서 입력된 json 데이터를 DTO로 자동으로 변환해 줄때 오류 발생 함.

InputStream에서 데이터를 읽으면 해당 데이터는 소비되므로 컨트롤러에서 다시 사용할 수 없다는 점입니다. 
따라서 필터에서 요청 데이터를 로깅하고 남기려면 데이터를 따로 저장하는 방식을 사용해야 합니다. 

[해결책] XSSFilter를 이용하여 InputStream() 호출시 입력데이터 가지고 있다가 로그 저장하도록 함. LoggingFilter는 미사용