Spring/Spring Security

[Spring Security] 필터 체인과 커스텀 필터

꾸준함. 2024. 10. 8. 13:01

HTTP 필터

  • 애플리케이션의 HTTP 요청이 처리되기 전에 다양한 보안 검사를 수행하여 인증 및 인가 등의 작업을 처리
  • Spring Security는 다양한 필터들을 체인 형태로 연결하여 요청이 처리될 때 순차적으로 실행되도록 구성하며 각 필터는 특정 보안 작업을 수행하며, 그 결과를 다음 필터에 전달함
  • 전체 필터 체인은 SecurityFilterChain 인터페이스에 의해 관리됨
    • 기본 설정으로 되어 있는 필터와 커스텀하게 설정된 필터를 함께 사용하여 HTTP 요청을 필터링
    • 실무의 다양한 비즈니스 요구사항을 구현하기에는 기본 필터 구성만으로는 부족하기 때문에 커스텀하게 필터를 정의하는 케이스가 많음

 

  • 필터 체인은 필터가 작동하는 순서가 정의된 필터의 모음을 나타내며 필터 구현체에는 각자의 순서가 존재
    • 각 필터는 순서가 존재하기 기본 필터들은 미리 정의된 필터의 순서 번호를 가지고 있음
    • 스프링 시큐리티가 제공하는 필터 이외의 커스텀 필터를 기본 필터 체인 앞/뒤로 추가 혹은 기본 체인을 대체하도록 설정 가능
    • 주의: 같은 위치에 두 개 이상의 필터가 적용될 수 있으며 동일한 위치일 경우 필터의 순서는 보장되지 않음 , 동일한 위치에 필터의 순서를 적용하더라도 절대 해당 위치에 존재하는 필터와 대체되는 것이 아님
    • 따라서 필터 체인에 필요가 없는 필터는 절대 추가해서는 안됨

 

HTTP 필터 체인 동작 방식

스프링 시큐리티의 HTTP 필터 체인은 요청이 애플리케이션으로 전달되기 전에 요청을 가로챈 뒤 정의된 보안 작업을 수행하고 다음 필터로 전달합니다.

  • 클라이언트가 웹 애플리케이션에 HTTP 요청 전송
  • 요청이 필터 체인에 인입되며 첫 번째 필터부터 순차적으로 실행
  • 각 필터는 자신의 작업을 수행한 후 요청을 다음 필터로 전달
  • 인증, 권한 부여, CSRF 방어 등 다양한 보안 검사를 진행한 뒤 모든 필터가 실행되면 컨트롤러로 요청 전달
  • 컨트롤러가 응답을 생성하면 필터 체인 내 마지막 필터가 응답을 클라이언트로 전달

 

https://bwgjoseph.com/spring-security-custom-pre-authentication-flow

 

Spring Security의 주요 HTTP 필터

 

1. SecurityContextPersistenceFilter

  • HTTP 요청 간 SecurityContext를 저장하고 요청이 다시 들어왔을 때 복원하는 역할 수행
  • 일반적으로 세션에 SecurityContext를 저장하며 요청마다 인증된 사용자 정보 등을 불러옴

 

2. UsernamePasswordAuthenticationFilter

  • 기본적인 Form 기반 로그인 요청을 처리하는 필터
  • 클라이언트가 제출한 username과 password를 인증하여 성공 시 인증 객체를 SecurityContext에 저장
  • 해당 필터는 기본적으로 /login URL로 들어오는 POST 요청 처리

 

3. BasicAuthenticationFilter

  • HTTP 기본 인증을 처리하는 필터
  • 요청의 Authorization 헤더에 포함된 Base64 기반 인코딩된 username과 password를 디코딩하여 인증 작업 수행

 

4. BearerTokenAuthenticationFilter

  • OAuth2 기반의 Bearer Token 인증을 처리하는 필터
  • 요청 헤더의 Authorization 값으로 토큰을 받아 처리하며 주로 API 인증 시 사용

 

5. CsrfFilter

  • CSRF(Cross-Site Request Forgery) 공격을 방지하기 위한 필터
  • POST, PUT, DELETE 등의 요청에 대해 CSRF 토큰을 검사하여 CSRF 공격 방지

 

6. LogoutFilter

  • 로그아웃 요청을 처리하는 필터
  • 기본적으로 /logout 경로로 들어오는 요청을 처리하며 세션을 무효화하고 인증된 사용자 정보를 제거함

 

7. FilterSecurityInterceptor

  • 권한 부여(Authorization)와 관련된 필터
  • AccessDecisionManager를 사용하여 사용자가 특정 자원에 대한 접근 권한이 있는지 확인
    • 권한이 없는 경우 접근을 차단하고 필요한 경우 적절한 예외를 발생시킴

 

커스텀 필터 구현 방법

앞서 언급했다시피 실무의 다양한 비즈니스 요구사항을 구현하기에는 기본 필터 구성만으로는 부족하기 때문에 커스텀하게 필터를 정의하는 케이스가 많기 때문에 커스텀 필터 구현 방법을 숙지해야 합니다.

  • 필터를 생성하기 위해서는 Spring 6 기준 jakarta.servlet 패키지의 Filter 인터페이스 구현체를 정의하고 doFilter() 메서드를 재정의 해야 함
  • doFilter 메서드는 ServletRequest, ServletResponse, 그리고 FilterChain을 파라미터로 전달받음
    • ServletRequest: HTTP 요청
    • ServletResponse: HTTP 응답, 클라이언트로 전달하기 전 응답 변경 가능
    • FilterChain: 필터 체인, 체인의 다음 필터로 전달하기 위한 용도

 

 

  • 스프링 시큐리티에서는 Filter 인터페이스를 까사는 추상 클래스인 GenericFilterBean을 제공하고 GenericFilterBean을 확장한 OncePerRequestFilter 추상 클래스도 제공
    • GenericFilterBean, OncePerRequestFilter는 추가 기능을 제공하기 때문에 잘 활용하면 유용함 
    • OncePerRequestFilter는 요청 당 한 번만 실행하도록 보장하기 때문에 이런 특성을 지니게 하려면 커스텀 필터를 OncePerRequestFilter 구현체로 정의해야 함
    • OncePerRequestFilter 구현체 외 다른 필터들은 요청 당 한 번만 실행하도록 보장하지 않음

 

 

커스텀 필터 예제 코드

  • BasicAuthenticationFilter 전에 HTTP 요청 헤더에 'Request-Id' 필드가 있는지 확인하는 RequestValidationFilter 추가
  • BasicAuthenticationFilter와 동일한 위치에 테스트 로그를 찍는 TestFilter 추가
  • BasicAuthenticaitonFilter 이후에 HTTP 요청 헤더에 존재하는 'Request-Id' 필드 값을 로깅하는 LoggingFilter 추가

 

 

부연 설명

  • shouldNotFilter 메서드를 재정의할 경우 요청에 따라 필터 적용 유무를 정할 수 있음
    • true가 반환되면 해당 필터를 적용하지 않음
    • 비동기 요청에 대해서는 적용되지 않음

 

  • 비동기 요청에 대해서는 shouldNotFilterAsyncDispatch를 재정의하여 처리 가능

 

참고

https://fastcampus.co.kr/classroom/240071

반응형