HTTP 요청을 처리하고 보안 관련 작업을 수행하기 위해 사용하는 핵심 구성 요소입니다. Spring Security는 Servlet Filter 체인을 기반으로 작동하며, 요청이 애플리케이션으로 들어오기 전에 이를 가로채고 필요한 인증 및 권한 부여 작업을 처리합니다.
FilterChain
Spring Security는 요청 처리의 순서를 정의하는 FilterChain을 구성합니다. 이 체인에서 여러 필터가 차례로 실행되며, 각각의 필터는 특정 보안 작업을 처리합니다.
DelegatingFilterProxy
Spring Security는 DelegatingFilterProxy를 사용해 Spring 컨텍스트 내에서 관리되는 필터 빈을 Servlet 컨테이너의 필터 체인과 연결합니다.
Security Filter Chain
Spring Security에서 설정한 필터 체인은 요청 URL에 따라 다르게 동작할 수 있습니다. 보안 설정에 따라 하나 이상의 필터 체인이 동작하며, 각각의 체인은 특정 요청 경로에 적용됩니다.
Spring Security는 요청을 처리하기 위해 여러 필터를 제공하며, 이를 조합하여 보안 기능을 구현합니다. 중요한 필터들은 다음과 같습니다:
인증 정보를 보존 및 복원하는 역할을 합니다.
요청이 들어올 때 SecurityContext를 복원하고, 요청 처리 후에 이를 저장합니다.
기본 폼 로그인에서 사용됩니다.
사용자 이름과 비밀번호를 검증하고 인증 처리를 수행합니다.
HTTP Basic 인증을 처리합니다.
요청 헤더에서 인증 정보를 가져와 인증 작업을 수행합니다.
JWT와 같은 Bearer Token을 사용한 인증을 처리합니다.
요청 헤더의 토큰을 확인하고 인증 정보를 설정합니다.
인증 또는 권한 부여 중 발생한 예외를 처리합니다.
예외를 적절한 HTTP 응답 코드로 변환하거나 인증 절차로 리디렉션합니다.
권한 부여(Authorization)를 처리하는 필터입니다.
접근 제어 규칙에 따라 사용자의 요청이 허용되는지 확인합니다.
로그아웃 요청을 처리합니다.
세션 무효화 및 인증 정보 제거와 같은 작업을 수행합니다.
CSRF(Cross-Site Request Forgery) 공격을 방지합니다.
요청에 포함된 CSRF 토큰의 유효성을 확인합니다.
OAuth2 기반 로그인 요청을 처리합니다.
인증 서버로부터 받은 정보를 검증하고 인증 객체를 생성합니다.
요청 수신
클라이언트로부터 HTTP 요청이 들어옵니다.
필터 체인 실행
요청이 Spring Security의 필터 체인을 통과하며, 각 필터는 특정 작업(인증, 권한 확인 등)을 수행합니다.
인증 및 권한 부여
적절한 인증 정보가 있는지 확인하고, 요청이 허용되는지 검사합니다.
컨트롤러로 전달
요청이 허용되면 애플리케이션의 컨트롤러로 전달됩니다.
사용자는 필요한 경우 Spring Security 필터를 커스텀하여 추가적인 보안 로직을 구현할 수 있습니다.
@Component
public class CustomFilter extends GenericFilterBean {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
// 커스텀 로직 처리
HttpServletRequest httpRequest = (HttpServletRequest) request;
System.out.println("Request URI: " + httpRequest.getRequestURI());
// 다음 필터로 전달
chain.doFilter(request, response);
}
}
필터를 SecurityFilterChain에 등록하려면 다음과 같이 설정합니다:
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http.addFilterBefore(new CustomFilter(), UsernamePasswordAuthenticationFilter.class);
return http.build();
}
GenericFilterBean란?
Spring Security에서 제공하는 GenericFilterBean은 Spring Framework의GenericFilter 인터페이스를 구현한 추상 클래스입니다. 이는 Servlet API의 Filter 인터페이스를 Spring 환경에 맞게 확장한 형태로, Spring의 빈 관리와 통합된 필터를 작성할 수 있도록 지원합니다.
Spring 컨테이너가 관리하는 Bean으로 등록되며, Spring의 설정과 라이프사이클 관리가 가능합니다.
필터를 구현할 때 doFilter 메서드만 오버라이드하면 됩니다.
추가적인 초기화 작업이 필요하다면 init 또는 afterPropertiesSet 메서드를 사용할 수 있습니다.
Spring 컨테이너가 관리하기 때문에 다른 Spring Bean을 주입받아 사용할 수 있습니다.
예외 처리나 요청/응답 전후 로직을 쉽게 작성할 수 있습니다.
스프링 시큐리티는 여러 개의 필터로 구성된 필터 체인을 통해 요청을 처리합니다.
UsernamePasswordAuthenticationFilter: 기본 인증 처리
BasicAuthenticationFilter: HTTP Basic 인증 처리
SecurityContextPersistenceFilter: 보안 컨텍스트 유지
OncePerRequestFilter는 요청당 한 번만 실행되는 필터를 구현할 수 있도록 도와줍니다.
import org.springframework.web.filter.OncePerRequestFilter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class CustomFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
// 요청 정보 로깅
String path = request.getRequestURI();
System.out.println("Requested Path: " + path);
// 다음 필터로 요청 전달
filterChain.doFilter(request, response);
}
}
Spring Security의 필터 체인에 커스텀 필터를 등록하려면 SecurityFilterChain을 구성합니다.
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.web.SecurityFilterChain;
@Configuration
public class SecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(authorize -> authorize
.anyRequest().authenticated()
)
.addFilterBefore(new CustomFilter(), UsernamePasswordAuthenticationFilter.class); // 필터 추가
return http.build();
}
}
addFilterBefore: 지정한 필터 앞에 커스텀 필터를 추가
addFilterAfter: 지정한 필터 뒤에 커스텀 필터를 추가
addFilterAt: 필터 체인의 특정 위치에 필터를 추가
.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
여기서 jwt가 먼저 실행되고 UsernamePasswordAuthenticationFilter 필터가 실행이 되는데 jwtAuthentication에서 인증이 된 경우 , UsernamePasswordAuthenticationFilter 필터는 어떻게 되는건가요?
jwtAuthenticationFilter가 UsernamePasswordAuthenticationFilter 전에 실행되도록 설정된 경우, jwtAuthenticationFilter가 먼저 요청을 처리하고 인증을 수행합니다.
만약 jwtAuthenticationFilter에서 인증이 성공적으로 처리되면, SecurityContextHolder에 인증된 사용자 정보가 저장됩니다.
그런데 UsernamePasswordAuthenticationFilter는 기본적으로 로그인 시 사용자의 아이디와 비밀번호를 처리하는 필터입니다.
만약 jwtAuthenticationFilter에서 인증이 이미 완료되었다면, UsernamePasswordAuthenticationFilter는 추가적인 인증을 시도하지 않습니다.
jwtAuthenticationFilter:
이 필터는 JWT 토큰을 확인하고, 유효한 경우 인증을 처리합니다.
인증이 성공하면 SecurityContextHolder에 Authentication 객체를 설정합니다.
UsernamePasswordAuthenticationFilter:
jwtAuthenticationFilter가 인증을 완료한 후에 실행됩니다.
이미 인증된 상태이므로, SecurityContextHolder에 Authentication이 존재한다면 UsernamePasswordAuthenticationFilter는 아무 작업도 하지 않습니다.
즉, UsernamePasswordAuthenticationFilter는 인증된 사용자가 있으면 인증을 시도하지 않고 그냥 지나갑니다.
이 동작은 Spring Security가 SecurityContextHolder에 이미 인증된 사용자 정보가 있는 경우, 후속 인증 필터들이 더 이상 인증을 시도하지 않도록 설계되어 있기 때문입니다.
클라이언트가 요청을 보낼 때 JWT 토큰을 보내면, jwtAuthenticationFilter가 그 토큰을 검증하고 유효한 경우 인증을 처리하여 SecurityContextHolder에 인증 정보를 설정합니다.
그 후, UsernamePasswordAuthenticationFilter가 실행되지만, SecurityContextHolder에 인증 정보가 이미 존재하면 인증을 생략하고 넘어갑니다.
따라서 JWT 인증 후, UsernamePasswordAuthenticationFilter는 별도로 인증을 진행하지 않고 넘어가게 됩니다.
필터의 순서는 중요합니다. 인증이나 권한 부여가 필요한 작업보다 앞서 실행되도록 해야 합니다.
CSRF와 같은 기본 제공 필터는 불필요하게 비활성화하지 않도록 주의합니다.
커스텀 필터는 반드시 기존 체인의 흐름을 방해하지 않도록 설계해야 합니다.
Spring Security의 필터 체인을 이해하고 적절히 활용하면 애플리케이션 보안을 더욱 강력하게 구현할 수 있습니다.