[Spring Boot] Filter

이용구·2023년 11월 9일

배경

SNS 로그인 기능을 만들다가 Spring Security에 Filter
용어를 보고 개념정리를 위해 작성하였다.



Filter?

스프링에서 DispatherServlet 이전에 위치하여 HTTP 요청과 응답을 수정할 수 있는 기능을 제공한다.

Filter와 Interceptor가 공통적인 작업을 처리하는 부분에서 비슷하다.

간단히 필터는 DispatherServlet 이전에 있기 때문에 Spring이 제공하는 기능이 아니다. 또한 http 요청과 응답을 수정 가능하다.

Interceptor은 Spring이 Dispatcher Servlet 요청을 받은 이후에 동작하게 된다.


Filter는 javax.servlet.Filter 인터페이스를 구현하여 만들어진다.

스프링과 무관하게 작업이 가능하다.



메서드

필터는 init, doFilter, distroy 3가지 메서드를 가진다.

  • init

init 메서드는 Filter의 초기화를 담당한다.

default 메서드이기에 반드시 구현할 필요는 없다.


  • doFilter

보통 이 doFilter 메서드에 로직을 추가한다.

FilterChain의 doFilter 메서드를 통해 다음 대상으로 요청을 전달한다.

chain.doFilter() 전/후로 우리가 원하는 과정을 추가해준다.



  • distroy

destroy 메소드는 필터 객체를 제거하고 반환한다.




구현

  • Controller
@Slf4j
@RestController
@RequestMapping("/api/user")
public class ApiController {

    @PostMapping("")
    public User user(@RequestBody User user){
        /*log.info("User : {}",user);*/
        return user;
    }
}

//user은 이름과 나이를 가진다.

  • Filter
@Slf4j
@Component
public class GlobalFilter implements Filter {
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {


        HttpServletRequest httpServletRequest = (HttpServletRequest)request;
        HttpServletResponse httpServletResponse = (HttpServletResponse) response;

        String url = httpServletRequest.getRequestURI();

        BufferedReader br = httpServletRequest.getReader();

        br.lines().forEach(line -> {
            log.info("url : {}, line : {}", url, line);
        });

        chain.doFilter(httpServletRequest, httpServletResponse);
    }
}

body를 다 읽어버려서 Contrller의 user객체에 매핑이 안된다.

Read를 한번 해버리면 더 읽지 못한다...



구글링...

ContentCashingRequestWrapper을 사용하는 방법으로

캐싱을 통해 내용을 계속 읽을 수 있다.

ContentCashingRequestWrapper 생성자에서 컨텐츠의 길이만 지정하과 실제 내용은 복사하지 않는다.

doFilter 일어난 후에 Spring이 안에서 모두 매핑 한 후에 읽어야 한다.



Controller


@Slf4j
@Component
public class GlobalFilter implements Filter {
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {


        //res, req 변경 가능
        ContentCachingRequestWrapper  httpServletRequest = new ContentCachingRequestWrapper((HttpServletRequest)request);
        ContentCachingResponseWrapper httpServletResponse = new ContentCachingResponseWrapper((HttpServletResponse)response);

        chain.doFilter(httpServletRequest,httpServletResponse); //doFilter 이후

        String uri = httpServletRequest.getRequestURI();


        String reqContent = new String(httpServletRequest.getContentAsByteArray());
        log.info("resquest url : {}, request body : {}", uri, reqContent);


        String resContent = new String(httpServletResponse.getContentAsByteArray());
        int httpStatus = httpServletResponse.getStatus();

        log.info("response status : {}, responseBody : {}", httpStatus, resContent);

        httpServletResponse.copyBodyToResponse(); //response body도 읽으면 내용이 없기 때문에 복사하는 메서드

    }
}

결과가 잘 나온다

profile
베짱이는 개미가 밉다

0개의 댓글