GenericFilterBean 과 OncePerRequestFilter

728x90

GenericFilterBean 과 OncePerRequestFilter

GenericFilterBean vs OncePerRequestFilter

  • SecurityFilterChain 에 등록된 필터는 GenericFilterBean 기반과 OncePerRequestFilter 기반으로 나뉜다.
  • 두 방식의 차이는 "한 번의 클라이언트 요청" 기준에서 동작 방식이 다르다는 점이다.
필터 종류 실행 방식
GenericFilterBean 같은 요청에서 여러 번 실행될 수 있음
OncePerRequestFilter 같은 요청에서 한 번만 실행됨

GenericFilterBean 의 동작 방식

  • 요청이 같은 필터를 여러 번 통과할 경우, 통과한 횟수만큼 실행된다.
  • 즉, 클라이언트의 한 번 요청에 대해 여러 번 필터가 실행될 수 있다.

Custom GenericFilterBean 예제

package io.github.nimkoes.ssis.filter;

import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
import org.springframework.web.filter.GenericFilterBean;

import java.io.IOException;

public class CustomGenericFilter extends GenericFilterBean {
  @Override
  public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
    System.out.println("CustomGenericFilter");
    chain.doFilter(request, response);
  }
}

OncePerRequestFilter 의 동작 방식

  • 요청이 같은 필터를 여러 번 통과하더라도, 첫 번째 요청에서만 실행됨.
  • 중복 실행을 방지하고 한 번만 실행되도록 보장하는 필터.

Custom OncePerRequestFilter 예제

package io.github.nimkoes.ssis.filter;

import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.web.filter.OncePerRequestFilter;

import java.io.IOException;

public class CustomOnceFilter extends OncePerRequestFilter {
  @Override
  protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
    System.out.println("CustomOnceFilter");
    filterChain.doFilter(request, response);
  }
}

같은 요청에서 여러 번 필터를 통과하더라도, 한 번만 실행됨.

대부분의 블로그가 잘못 적은 내용

  • 많은 블로그에서 OncePerRequestFilter 가 리디렉트(302 응답)에서도 한 번만 실행된다고 설명하지만, 이는 잘못된 정보다.
  • 리디렉트(302 응답)와 Forward 의 차이를 이해해야 한다.
상태 OncePerRequestFilter 실행 여부
Forward 한 번만 실행됨
Redirect (302 응답) 새 요청이므로 다시 실행됨

각 상태에서 필터의 동작 방식

예제 코드

SecurityConfig.java

package io.github.nimkoes.ssis.config;

import io.github.nimkoes.ssis.filter.CustomGenericFilter;
import io.github.nimkoes.ssis.filter.CustomOnceFilter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.logout.LogoutFilter;

@Configuration
@EnableWebSecurity(debug = true)
public class SecurityConfig {

  @Bean
  public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
    http
      .authorizeHttpRequests(auth -> auth.anyRequest().permitAll())
      .addFilterAfter(new CustomGenericFilter(), LogoutFilter.class)
      .addFilterAfter(new CustomOnceFilter(), LogoutFilter.class);

    return http.build();
  }
}

SampleController.java

package io.github.nimkoes.ssis.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
public class SampleController {

  @GetMapping("/test-filter-before-forward")
  public String beforeForward() {
    return "forward:/test-filter-after";
  }

  @GetMapping("/test-filter-before-redirect")
  public String beforeRedirect() {
    return "redirect:/test-filter-after";
  }

  @GetMapping("/test-filter-after")
  @ResponseBody
  public String after() {
    return "hello security!";
  }
}

Forward 상태

  • Forward 는 서버 내부에서 다른 URL로 요청을 넘기는 방식이다.
  • 클라이언트의 요청은 여전히 한 번이므로, OncePerRequestFilter 도 한 번만 실행된다.

### forward
GET localhost:8080/test-filter-before-forward
2025-03-09T13:38:45.081+09:00  INFO 93807 --- [nio-8080-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring DispatcherServlet 'dispatcherServlet'
2025-03-09T13:38:45.081+09:00  INFO 93807 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet        : Initializing Servlet 'dispatcherServlet'
2025-03-09T13:38:45.082+09:00  INFO 93807 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet        : Completed initialization in 1 ms
2025-03-09T13:38:45.088+09:00 DEBUG 93807 --- [nio-8080-exec-1] o.s.security.web.FilterChainProxy        : Securing GET /test-filter-before-forward
CustomGenericFilter
CustomOnceFilter
2025-03-09T13:38:45.091+09:00 DEBUG 93807 --- [nio-8080-exec-1] o.s.security.web.FilterChainProxy        : Secured GET /test-filter-before-forward
2025-03-09T13:38:45.112+09:00 DEBUG 93807 --- [nio-8080-exec-1] o.s.security.web.FilterChainProxy        : Securing GET /test-filter-after
CustomGenericFilter
2025-03-09T13:38:45.112+09:00 DEBUG 93807 --- [nio-8080-exec-1] o.s.security.web.FilterChainProxy        : Secured GET /test-filter-after
2025-03-09T13:38:45.118+09:00 DEBUG 93807 --- [nio-8080-exec-1] o.s.s.w.a.AnonymousAuthenticationFilter  : Set SecurityContextHolder to anonymous SecurityContext

Redirect 상태

  • Redirect(302 응답)는 서버가 클라이언트에게 새로운 요청을 보내도록 지시하는 방식이다.
  • 클라이언트가 다시 요청을 보내므로, OncePerRequestFilter 가 다시 실행된다.

### forward
GET localhost:8080/test-filter-before-redirect
2025-03-09T13:38:50.952+09:00 DEBUG 93807 --- [nio-8080-exec-2] o.s.security.web.FilterChainProxy        : Securing GET /test-filter-before-redirect
CustomGenericFilter
CustomOnceFilter
2025-03-09T13:38:50.952+09:00 DEBUG 93807 --- [nio-8080-exec-2] o.s.security.web.FilterChainProxy        : Secured GET /test-filter-before-redirect
2025-03-09T13:38:50.955+09:00 DEBUG 93807 --- [nio-8080-exec-2] o.s.s.w.a.AnonymousAuthenticationFilter  : Set SecurityContextHolder to anonymous SecurityContext
2025-03-09T13:38:50.971+09:00 DEBUG 93807 --- [nio-8080-exec-3] o.s.security.web.FilterChainProxy        : Securing GET /test-filter-after
CustomGenericFilter
CustomOnceFilter
2025-03-09T13:38:50.971+09:00 DEBUG 93807 --- [nio-8080-exec-3] o.s.security.web.FilterChainProxy        : Secured GET /test-filter-after
2025-03-09T13:38:50.972+09:00 DEBUG 93807 --- [nio-8080-exec-3] o.s.s.w.a.AnonymousAuthenticationFilter  : Set SecurityContextHolder to anonymous SecurityContext
728x90

'시리즈 > Spring Security' 카테고리의 다른 글

필터 상속과 요청 전파  (1) 2025.05.05
SecurityContextHolder  (0) 2025.05.04
SecurityFilterChain 구조  (0) 2025.05.03
SecurityFilterChain 등록  (0) 2025.05.02
Spring Security 동작 원리 개요  (0) 2025.05.01