서블릿 필터(Servlet Filter)

KDG: First things first!·2024년 9월 26일
0

Spring

목록 보기
4/5

이전 포스팅인 웹 애플리케이션 인증 방식 1 : 쿠키-세션, 웹 애플리케이션 인증 방식 2 : JWT에 이어집니다.



서블릿 필터

서블릿 필터(Servlet Filter) : Java 웹 애플리케이션에서 클라이언트의 요청(request)과 서버의 응답(response)을 가로채서 처리할 수 있는 도구이다. 필터는 요청이 서블릿(Servlet)에 도달하기 전 또는 응답이 클라이언트에게 전달되기 직전에 실행된다. 이를 통해 요청/응답 데이터를 변환하거나, 인증/인가를 확인하여 접근을 제어하고, 로깅을 수행하는 등의 작업을 수행한다.


스프링 내부에는 jakarta.servlet.Filter 인터페이스를 통해 구현된 서블릿 필터가 존재한다. 해당 인터페이스는 클라이언트의 요청을 가로채어 처리할 수 있도록 하는 여러 메서드들을 제공한다.


[서블릿 필터 예시]


import jakarta.servlet.Filter;
import jakarta.servlet.FilterChain;
import jakarta.servlet.FilterConfig;
import jakarta.servlet.ServletException;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
import jakarta.servlet.annotation.WebFilter;
import java.io.IOException;
import java.util.logging.Logger;

@WebFilter(urlPatterns = "/*")  // 모든 요청에 대해 필터 적용
public class ExampleFilter implements Filter {

    private final Logger logger = Logger.getLogger(ExampleFilter.class.getName());

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        // 필터 초기화 작업 (필요시)
        logger.info("ExampleFilter initialized");
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        // 요청 처리 전
        logger.info("ExampleFilter: Request processing started");

        // 다음 필터로 요청을 전달하거나 서블릿에 전달
        chain.doFilter(request, response);

        // 응답 처리 후
        logger.info("ExampleFilter: Response processing completed");
    }

    @Override
    public void destroy() {
        // 필터 종료 작업 (필요시)
        logger.info("ExampleFilter destroyed");
    }
}


서블릿 필터의 핵심 메서드들

  • @WebFilter(urlPatterns = "/*"): 필터가 적용될 URL 패턴을 지정한다. "/*"을 지정하면 모든 요청에 대해 필터가 적용된다.
  • init(FilterConfig filterConfig): 필터의 인스턴스가 생성된 후 처음에 딱 한 번 호출되는 서블릿 필터의 초기화 메서드이다. FilterConfig 객체를 통해 필터 이름, 초기화 매개변수, 서블릿 컨텍스트같은 필터 초기화 매개변수를 읽어올 수 있다.
  • doFilter(ServletRequest request, ServletResponse response, FilterChain chain): 각 요청이나 응답이 들어올 때마다 호출되는 필터의 핵심 메서드로 ServletRequestServletResponse 객체를 통하여 요청과 응답을 처리한 후 다음 필터나 최종 대상(서블릿)으로 요청을 전달한다.
    (필터는 여러 필터가 순서대로 엮여 있는 사슬(Chain)모양을 하고 있다.)

    • chain.doFilter():

      • 1. 요청 전 처리: doFilter() 메서드의 chain.doFilter() 호출 이전은 요청을 처리하거나 가로채는 로직이다.

      • 2. 요청 전달: chain.doFilter(request, response) 호출은 요청을 다음 필터로 전달한다. 만약 다음 필터가 없다면 서블릿으로 요청이 전달된다.

      • 3. 응답 후 처리: chain.doFilter() 호출 이후는 요청이 처리되어 다시 돌아온 응답을 처리하거나 수정하는 로직이다.
  • destroy(): 필터가 더 이상 사용되지 않을 때 마지막으로 호출되는 메소드이다. 이 메소드는 필터가 리소스를 정리하고, 열린 연결을 닫거나, 메모리에서 할당된 객체를 해제하는 등의 마무리 작업을 수행하는 데 사용된다.


서블릿 필터 라이프 사이클

inti() 메서드로 필터를 초기화하여 설정하고 doFilter()로 요청을 검사, 처리하고 다음 필터나 서블릿으로 넘겨준 후 요청이 처리되어 돌아온 응답을 처리하거나 수정한 후 destory() 메서드로 마무리 작업을 수행한 후 소멸된다.


FilterChain은 Java Servlet API에서 서블릿 필터의 동작을 관리하고 필터 간의 요청과 응답을 연결하는 역할을 하는 인터페이스이다. 필터 체인은 여러 필터가 순차적으로 요청을 처리할 수 있도록 도와준다.

  • 여러 필터의 연결: FilterChain은 여러 필터를 연결하여 요청이 필터 체인을 통해 순차적으로 처리되도록 한다. 각 필터는 다음 필터 또는 최종 서블릿으로 요청을 전달할 수 있다.

  • 요청과 응답 전달: 필터는 FilterChain을 사용하여 chain.doFilter(ServletRequest request, ServletResponse response) 요청과 응답 객체를 다음 필터 또는 서블릿으로 전달한다. 이 메소드가 호출되지 않으면, 요청은 필터 체인을 통과하지 않고 종료된다. 해당 메커니즘을 통해 필터 간에 데이터를 주고받을 수 있다.

  • 체인 흐름 제어: 각 필터는 FilterChain을 통해 자신의 처리 후 다음 필터로 제어를 넘길 수 있으며, 필요할 경우 추가적인 후처리를 수행할 수 있다.



Filter 등록하기(Spring Security 사용 X)

[필터 클래스 구현]

import jakarta.servlet.Filter;
import jakarta.servlet.FilterChain;
import jakarta.servlet.FilterConfig;
import jakarta.servlet.ServletException;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
import jakarta.servlet.http.HttpServletRequest;
import java.io.IOException;

public class CustomFilter implements Filter {

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        // 필터 초기화 로직 (필요하다면)
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest req = (HttpServletRequest) request;
        System.out.println("Request URI is: " + req.getRequestURI());

        // 다음 필터로 요청을 전달
        chain.doFilter(request, response);
    }

    @Override
    public void destroy() {
        // 필터 종료 시의 로직 (필요하다면)
    }
}
  
  


1. @Bean을 사용한 필터 등록


  
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import jakarta.servlet.Filter;

@Configuration
public class FilterConfig {

    @Bean
    public Filter customFilter() {
        return new CustomFilter();
    }
}
  
  

설정을 나타내는 @Configuration 클래스를 생성하고 필터를 @Bean을 통해 수동으로 필터를 빈으로 등록한다.



2. FilterRegistrationBean로 필터 등록

서블릿 필터를 등록하고 설정하는 데 사용되는 클래스인 FilterRegistrationBean을 사용하면 필터를 특정 URL 패턴에만 적용하거나 필터의 순서를 지정할 수 있기 때문에 필터를 더 세밀하게 제어할 수 있다는 장점이 있다.


import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class FilterConfig {

    @Bean
    public FilterRegistrationBean<CustomFilter> loggingFilter() {
        FilterRegistrationBean<CustomFilter> registrationBean = new FilterRegistrationBean<>();

        registrationBean.setFilter(new CustomFilter());
        registrationBean.addUrlPatterns("/api/*"); // 특정 URL 패턴에만 필터 적용
        registrationBean.setOrder(1); // 필터의 순서 지정

        return registrationBean;
    }
}
  
  


3. @WebFilter를 통한 필터 등록과 @ServletComponentScan을 통한 필터 활성화


[@WebFIlter()로 필터 클래스 구현 및 등록]


import jakarta.servlet.Filter;
import jakarta.servlet.FilterChain;
import jakarta.servlet.FilterConfig;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebFilter;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
import jakarta.servlet.http.HttpServletRequest;
import java.io.IOException;

@WebFilter(urlPatterns = "/api/*")
public class CustomFilter implements Filter {

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        // 초기화 로직 (필요하다면)
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest req = (HttpServletRequest) request;
        System.out.println("Request URI is: " + req.getRequestURI());

        // 다음 필터로 요청을 전달
        chain.doFilter(request, response);
    }

    @Override
    public void destroy() {
        // 필터 종료 로직 (필요하다면)
    }
}  
  

[@ServletComponentScan으로 필터 활성화]

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletComponentScan;

@SpringBootApplication
@ServletComponentScan
public class DemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
}
  
  

Application에 @ServletComponentScan를 붙여주면 Spring Boot가 자동으로 @WebFilter로 등록된 필터를 검색하고 등록해준다.

profile
알고리즘, 자료구조 블로그: https://gyun97.github.io/

0개의 댓글