[Spring Security 6.x] HttpSecurity / WebSecurity
목차
- HttpSecurity
- WebSecurity
1. HttpSecurity
① HttpSecurity의 생성
맨 처음 Spring Security 의존성이 추가된 Spring Boot Server가 기동을 하면, 여러 설정 클래스들을 읽어들이며
자동적으로 필요한 Bean을 등록하게 된다.
이때, HttpSecurityConfiguration 클래스에서 HttpSecurity를 Bean으로 등록하며 서버 기동을 시작한다.
HttpSecurity는 보안에 필요한 각 설정 클래스와 필터들을 생성하고, 최종적으로 Security Filter Chain Bean을 생성한다.
여기에서 HttpSecurity에는 Map의 형태로 여러 설정에 관한 Configure 클래스를 갖고 있는데, doBuild() 메서드를 통해 해당 설정들을 읽어 초기화를 진행하며 Filter를 만들어 SecurityFilterChain에 저장한다.
② SecurityFilterChain의 생성
Security의 SecurityFilterChain은 HttpSecurity에 의해 생성이되어 Bean으로 등록이 된다.
이 때, 사용자가 별도로 SecurityFilterChain에 대한 설정을 하지 않았다면 DefaultSecurityFilterChain 클래스가 Bean으로
등록이 되는데, 설정은 다음과 같다.
코드를 보면 HttpSecurity에서 모든 요청에 대해 인증처리를 진행한다는 내용이 담겨있다.
SecurityFilterChain은 ArrayList 형식으로 Filter들을 담고있고, 인터페이스의 기본 메서드는 총 2개로
1. boolean matches(HttpServletRequest request)
2. List<Filter> getFilters()
가 존재한다.
②-1 SecurityFilterChain의 getFilters()
List<Filter> getFilters() 메서드는 현재 SecurityFilterChain에 속해있는 Filter들을 담아놓은 List를 반환해주는 메서드다.
이 메서드를 통해 Filter Chain에 현재 어떤 Filter들이 속해있는지를 확인할 수 있고, 각 필터는 요청 처리 작업에 대해 특정 기능을 수행한다.
②-2 SecurityFilterChain의 matches()
matches() 함수는 여러 개의 SecurityFilterChain이 존재할 때, 사용자가 보낸 요청이 현재 SecurityFilterChain이 처리해야 하는지에 대한 참/거짓 여부를 반환한다.
true를 반환하면 해당 SecurityFilterChain에 의해 처리되어야 하는 요청임을 의미하고, false라면 다른 FilterChain이나 다른 처리 방식으로 해결되어야 하는 요청임을 의미한다.
즉, 이를 통해 특정 요청에 대한 적절한 필터 체인 선택을 고르는 것이 가능하게 해준다.
SecurityFilterChain은 인터페이스기 때문에 이를 구현한 구현체, 즉 DefaultSecurityFilterChain의 코드를 보면 다음과 같다.
여기에서 matches()함수는 클래스에 존재하는 RequestMatcher의 mathces()함수의 결과를 결과로 반환한다.
RequestMatcher역시 인터페이스기 때문에 이를 구현한 구현체의 matches함수를 통해 참 거짓 여부가 결정된다.
@ConditionalOnDefaultWebSecurity
static class SecurityFilterChainConfiguration {
SecurityFilterChainConfiguration() {
}
@Bean
@Order(2147483642)
SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
http.authorizeHttpRequests((requests) -> {
((AuthorizeHttpRequestsConfigurer.AuthorizedUrl)requests.anyRequest()).authenticated();
});
http.formLogin(Customizer.withDefaults());
http.httpBasic(Customizer.withDefaults());
return (SecurityFilterChain)http.build();
}
}
위의 코드는 DefaultSecurityFilterChain이 생성되고 빈으로 등록될 때의 설정인데, 코드를 살펴보면 requestMatcher에 대한 설정이 존재하지 않는 것을 볼 수 있다.
이 때 RequestMatcher는 구현체로 AnyRequsetMatcher를 사용한다.
AnyRequestMatcher의 코드를 살펴보면 해당 클래스의 matches 함수는 어떠한 요청에 대해서도 true를 반환하는 것을 확인할 수 있다.
즉, 어떠한 설정도 되어있지 않을 때 생성되는 DefaultSecurityFilterChain에서의 matches()함수는 어떤 요청에 대해서라도 해당 FilterChain이 처리해야함을 나타내는 것을 볼 수 있다.
즉, 정리하자면 다음 순서와 같다.
1. Client가 서버로 Request를 보낸다.
2. SecurityFilterChain은 Request에 포함되어있는 정보를 토대로 RequsetMatcher의 match()함수를 실행한다.
3. 이를 통해 해당 요청이 자신이 처리해야하 하는 것인지, 다른 FilterChain이 처리해야하 하는지를 확인한다.
4. 만약 자신이 처리해야하 한다면 doFilter()를 통해 여러 Filter들을 거쳐 이후 Servlet에 도달하게 된다.
2. WebSecurity
WebSecurity는 WebSecurityConfiguration 클래스에서 생성하고 초기화를 진행한다.
WebSecurity의 역할은 HttpSecurity에서 생성한 SecurityFilterChain Bean을 SecurityBuilder에 저장하고,
이후 WebSecurity의 build() 메서드를 통해 SecurityBuilder에서 FilterChain들을 꺼내어 FilterChainProxy를 생성한다.
즉, WebSecurity는 HttpSecurity보다 좀 더 상위의 개념이라고 볼 수 있다.
여기에서 FilterChainProxy는 최종적으로 모든 필터들의 목록을 갖고있으며, 이에 따라 Filter를 실행하는 객체다.
즉, SecurityFilterChain은 Filter들을 갖고있는 목록일 뿐이며, 이 목록을 토대로 클라이언트의 요청을 처리하는 것이
FilterChainProxy라는 것이다. SecurityFilterChain이 Filter의 역할을 해주는 것이 아니다.
따라서 HttpSecurity와 WebSecurity의 초기화 과정은 FilterChainProxy를 만들고 이 안에 SecurityFilterChain을 넣는 과정이라고 볼 수 있다.