본 게시물은 스스로의 공부를 위한 글입니다.
잘못된 내용이 있으면 댓글로 알려주세요!
Spring Bean은 Servlet Filter에 Injection이 불가능하다. (컨테이너가 서로 다르기 때문이다.)
그럼 스프링으로 만든 필터와 Servlet Filter을 어떻게 연결할까? 바로 DelegatingFilterProxy
이다.
DelegatingFilterProxy
는 Servlet Filter이다. 요청을 받게 되면 Spring Bean에게 요청을 위임하게 된다.
따라서 Spring 기술도 사용하면서 Filter역할로도 사용할 수 있게 된다.
만약 스프링 시큐리티를 사용한다면 springSecurityFilterChain으로 생성된 빈을 ApplicationContext에서 찾아서 요청을 위임하게 된다.
springSecurityFilterChain으로 생성되는 필터 빈은 FilterChainProxy이다.
즉, DelegatingFilterProxy에게 요청을 위임 받고 실제 보안 처리를 하는 필터이다.
스프링 시큐리티가 기본적으로 생성하는 필터도 있고, 설정 클래스에서 사용자가 API 추가 시 생성되는 필터도 있다.
필터들은 Chain으로 연결되어 있어서 사용자의 요청을 필터 순서대로 호출하여 전달하게 된다.
물론 사용자 정의 필터를 생성해서 기존의 필터 전, 후로 추가할 수 있다.
마지막 필터까지 인증 및 인가 예외가 발생하지 않으면 보안이 통과하게 된다. 즉, Dispatcher Servlet
으로 넘어가게 된다.
보안 설정 파일을 분리해서 여러개 작성할 수 있다. 그러면 FilterChainProxy
에 필터들이 List형태로 들어가게 되는데, 순서대로 설정파일의 RequestMacher와 현재 요청한 url이 부합한지 확인한다. 만약 적용 대상이라면 해당 필터를 진행하게 된다.
설정 클래스를 2개 작성해보자.
@Configuration
@EnableWebSecurity
@Order(0)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.antMatcher("/admin/**")
.authorizeRequests()
.anyRequest().authenticated()
.and()
.httpBasic();
}
}
@Configuration
@Order(1)
class SecurityConfig2 extends WebSecurityConfigurerAdapter{
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.anyRequest().permitAll()
.and()
.formLogin();
}
}
설정 클래스 위에 @Order( 숫자 )
를 찾을 수 있다. 숫자가 작을 수록 우선순위가 높은건데, 필터 체인 리스트에 들어가는 순서이다.
FilterChainProxy에서 List<Filter> getFilters(HttpServletRequest request)
에 break point를 놓고 디버깅을 해보면 아래와 같이 필터 체인 리스트에 우리가 등록한 2개의 필터가 넣어져 있는 것을 확인할 수 있다.
requestMatcher
을 보면 Index 0에는 pattern=‘/admin/**’
가 있고, Index 1에는 any request
가 있다. 스프링 시큐리티는 for(while) 문을 돌면서 해당 pattern에 맞는 필터를 찾아 리턴하게 된다.
다음과 같은 예시를 보자.
@Order(0) config1 : antMatcher(“/admin”)
@Order(1) config2 : anyRequest
1. `/admin`으로 접속 -> config1 실행
2. `/`으로 접속 -> config2 실행
@Order(1) config1 : antMatcher(“/admin”)
@Order(0) config2 : anyRequest
1. `/admin`으로 접속 -> config1 실행
2. `/`으로 접속 -> config1 실행
먼저 부합하는 필터 체인을 실행하기 때문에 @Order
로 순서를 정하는 것이 중요하다. (어노테이션이 없으면 오류난다.) 주로 좁은 범위(구체적인 경로)에게 우선순위를 높게 주는것이 일반적이다.
사실 하나의 SecurityConfig로도 구성이 가능하기는 하다만, 설정 클래스를 여러개로 나눔으로써 확장성 면에서 이점이 있다. 인증 방식을 완전히 다른 방식으로 설정하거나, 여러가지 필터나 보안 옵션 또한 다양하게 설정이 가능하다. 사용자 보안과 관리자 보안을 나누거나 도메인 별로 나누어서 관리할 수 있다.
인프런 '스프링 시큐리티 - Spring Boot 기반으로 개발하는 Spring Security' (정수원)