스프링 시큐리티에서 설정을 적용하는 기본적인 클래스에 대해 학습하고 분석하고자 합니다.
환경은 스프링 시큐리티 6.2.1 버전입니다.
스프링 시큐리티 설정 클래스를 생성하기 위한, 가장 기본적인 빌더 인터페이스입니다.
해당 글에서 살펴볼 SecurityBuilder의 구현체는 HttpSecurity, WebSecurity가 있습니다.
이 외에도 AuthenticationManagerBuilder 등이 SecurityBuilder를 구현합니다.
스프링 시큐리티에서 보안 관련된 필터들을 생성하는 인터페이스입니다.
특이 사항으로는 제네릭으로 SecurityBuiler를 받고 있다는 점입니다.
init() 메서드와 configure() 메서드를 확인하면, SecurityBuilder에 지정된 설정을 토대로 필터들을 생성하기 위해 SecurityBuilder를 파라미터로 받고 있음을 확인할 수 있습니다.
SecurityBuilder에서 설정된 내용을 필터로 구현해야 하기 때문에 상당히 많은 SecurityConfigurer가 존재합니다.
스프링 시큐리티 6.2.1 버전 기준으로는 총 48개의 SecurityConfigurer 구현체들이 존재합니다.
SecurityBuilder 구현체에서 다양한 SecurityConfigurer를 필드로 관리합니다.
이러한 SecurityConfigurer는 다양한 경로로 SecurityBuilder에 추가됩니다.
예를 들어, HttpSecurity에 csrf() 등 다양한 메서드를 통해 설정을 진행하면서 내부적으로 SecurityConfigurer를 등록합니다.
이렇게 등록된 SecurityConfigurer는 SecurityBuilder.build() 호출 시 SecurityConfigurer의 init()과 configure()를 호출해 초기화를 수행합니다.
이후 SecurityBuilder.build()를 호출할 때, 초기화된 SecurityConfigurer를 통해 필요한 필터를 추가합니다.
특정 HTTP 요청에 대한 인증/인가와 관련된 전반적인 설정들을 처리합니다.
스프링 시큐리티는 필터 기반 프레임워키이기 때문에, 설정을 수행할 수 있는 필터들을 생성한다는 의미와 동일합니다.
빌드 결과로 SecurityFilterChain의 구현체인 DefaultSecurityFilterChain을 반환합니다.
HttpSecurity는 HttpSecurityConfiguration에 의해 빈으로 등록됩니다.
추후 HttpSecurity를 주입받아 필요한 설정을 추가한 뒤, build()를 호출해 초기화된 SecurityConfigurer에 의해 추가된 필터를 관리하는 SecurityFilterChain을 생성해 반환합니다.
요청에 대한 보안 처리를 담당하는 필터 컬렉션입니다.
HttpSecurity에서 생성한 여러 필터를 관리한다고 볼 수 있습니다.
해당 인터페이스에 대한 구현체는 DefaultSecurityFilterChain만이 존재합니다.
내부적으로 요청을 분석하기 위한 RequestMatcher와 여러 필터를 가지고 있습니다.
우선, 클라이언트의 요청을 SecurityFilterChain의 RequestMatcher로 판단합니다.
일치한다면 SecurityFilterChain의 모든 필터를 통해 요청에 대한 전처리를 수행합니다.
전처리가 모두 끝났다면 Servlet에 도달해 요청을 수행하고 응답합니다.
HttpSecurityConfiguration은 HttpSecurity에 대한 기본 설정을 추가하고 빈으로 등록하는 Auto Configuration입니다.
위와 같이 HttpSecurity를 빈으로 등록합니다.
HttpSecurity가 생성한 SecurityFilterChain을 가진 FilterChainProxy을 생성합니다.
WebSecurity는 WebSecurityConfiguration에 의해 생성 및 초기화되며, WebSecurity.build()의 결과인 FilterChainProxy를 빈으로 등록합니다.
스프링 시큐리티에서 여러 SecurityFilterChain를 관리하는 핵심 컴포넌트입니다.
이 FilterChainProxy는 DelegatingFilterProxy의 필드로 관리되며, DelegatingFilterProxy는 WAS의 FilterChain으로 등록됩니다.
Servlet Container가 호출하는 보안 절차의 진입점이라고 볼 수 있습니다.
WebSecurityConfiguration는 내부적으로 WebSecurity를 생성하고 빌드해 FilterChainProxy를 빈으로 등록하는 설정 클래스입니다.
WebSecurityConfiguration은 주입받은 SecurityFilterChain이 없는 경우, HttpSecurity.build()를 직접 호출합니다.
이 때 기본적으로 formLogin() / httpBasic()을 활성화하며, 모든 요청에 대해 보안 절차를 요구하는 것을 확인할 수 있습니다.
SecurityFilterChain을 주입받았다면, 모든 SecurityFilterChain을 WebSecurity에 추가합니다.
SecurityBuilder의 대표적인 클래스, HttpSecurity와 WebSecurity의 계층 구조 및 코드를 살펴보겠습니다.
SecurityBuilder의 대표적인 클래스, HttpSecurity와 WebSecurity의 계층 구조를 확인해보면 다음과 같은 추상 클래스를 공통적으로 확장하고 있음을 확인할 수 있습니다.
이 두 추상 클래스에 대해 살펴보겠습니다.
빌드 중인 객체가 단 한 번 빌드되는 것을 보장하는 빌더입니다.
코드 상으로는 AtomicInteger를 활용한 CAS 연산을 통해 이를 보장하고 있습니다.
해당 클래스를 확장하는 빌더 클래스의 경우 추상 메서드인 doBuild()를 통해 빌드 시 어떤 동작을 수행할지 반드시 명시해야 합니다.
다양한 부가 기능을 제공하는, 빌드 시 저장할 SecurityConfigurer를 관리할 수 있는 구체적인 빌더입니다.
SecurityConfigurer를 관리하는 필드 및 공유할 수 있는 객체를 관리하는 필드를 확인할 수 있습니다.
AbstractConfiguredSecurityBuilder에서 가장 중요한 doBuild() 메서드입니다.
AbstractSecurityBuilder를 오버라이딩했으며, synchronized를 활용한 모니터락으로 동기화 문제를 방지하면서 필드를 통해 빌드 상태를 관리합니다.
init(), configure()를 통해 configurers로 관리하는 SecurityConfigure의 init() 메서드와 configure() 메서드를 호출합니다.
이 때, 제네릭 타입인 B로 명시적 형변환을 해 SecurityConfigure에 SecurityBuilder를 전달합니다.
해당 클래스를 확장하는 빌더 클래스의 경우 추상 메서드인 performBuild()를 통해 빌드 시 어떤 동작을 수행할지 반드시 명시해야 합니다.
HttpSecurity는 HttpSecurityBuilder 인터페이스를 구현하고 있습니다.
이름에서부터 알 수 있듯이 HTTP 통신을 처리하기 위한 SecurityBuilder 인터페이스입니다.
addFilter(), addFilterBefore(), addFilterAfter(), userDetailsService() 등 다양한 메서드가 존재합니다.
이러한 메서드들은 SecurityConfigurer나 필터 추가, RequestMatcher 추가 등 HTTP 통신 관련 설정을 수행합니다.
WebSecurity는 다음과 같은 Aware를 구현하고 있습니다.
즉, 위와 같이 WebSecurity가 build()를 통해 생성한 SecurityFilterChain이 WAS의 FilterChain에 등록되기 위해 ApplicationContext와 ServletContext에 접근해야 하기 때문이라고 볼 수 있습니다.
AbstractConfiguredSecurityBuilder 추상 클래스의 메서드이며, HttpSecurity는 빌드 시 수행할 동작을 해당 메서드에서 명시합니다.
ExpressionUrlAuthorizationConfigurer 혹은 AuthorizeHttpRequestsConfigurer가 존재하는지 체크합니다.
이름에서 유추할 수 있듯이 모두 인가와 관련된 SecurityConfigurer이며, ExpressionUrlAuthorizationConfigurer은 deprecated된 상태이며 AuthorizeHttpRequestsConfigurer로 대체되었습니다.
현재 버전에서는 둘 모두 지원하기 때문에, 둘 중 하나라도 존재하면 됩니다.
antMatcher(...).permitAll()과 같은 메서드에서 URL을 분석하고 지정한 권한을 토대로 특정 필터가 동작 수행할지 여부를 판단하는 Matcher 기능을 제공합니다.
필터를 추가합니다.
SecurityFilterChain의 구현체인 DefaultSecurityFilterChain을 생성해 반환합니다.
WebSecurity의 performBuild() 메서드는 HttpSecurity보다 양이 많습니다.
보안을 신경 쓸 필요가 없는 정적 컨텐츠, 검증을 하지 않아야 하는 요청, 성능 향상 등의 이유를 위해 인증/인가에서 제외할 요청을 등록합니다.
이러한 요청은 RequestMatcher를 통해 URL 매핑으로 체크하며, SecurityFilterChain으로 관리됩니다.
this.securityFilterChainBuilders처럼 상황에 맞는 여러 SecurityFilterChain을 생성하고 관리할 수 있습니다.
이러한 SecurityFilterChain은 RequestMatcherDelegatingWebInvocationPrivilegeEvaluator에 의해 관리됩니다.
권한 평가를 수행하는 PrivilegeEvaluator을 추가합니다.
반환할 FilterChainProxy를 생성하고 초기 값을 세팅합니다.
후처리 동작을 수행한 뒤 FilterChainProxy를 반환합니다.