Spring Security를 이해하기 위해 반드시 알아야할 것들
사이드 프로젝트를 개발할 때마다 서버 개발자들에게 가장 귀찮은 것은 인증 및 인가 구현이라고 생각합니다. 요구 사항에 따라 달라지는 도메인 영역과 달리 인증 및 인가는 대부분 동일하기 때문에 항상 반복되는 작업이기 때문입니다.
게다가 개발자에 따라서 인증 및 인가를 구현하는 방법도 다양할 수 있습니다. 이 경우 협업할 때 코드를 이해하는 노력이 추가로 필요하게 됩니다. Spring Framework는 이러한 문제를 해결하기 위하여 인증 및 인가 기능을 추상화하여 일관된 개발 방법을 제공하며 놓칠 수 있는 보안 기능까지 지원하는 Spring Security를 제공합니다.
이번 글에서는 Servlet 기반의 Spring Security의 아키텍처에 대해 알아보고 내부적인 동작의 흐름에 대해 다뤄보겠습니다. 참고 자료는 다음과 같습니다.
Spring Security의 핵심은 Filter
Servlet 기반의 Spring Security는 Servlet Filter로 구현이 됩니다. Servlet Filter는 FilterChain이라고 하는 Filter들의 묶음으로 연결되어있습니다. 클라이언트가 서버에 요청했을 때 이를 Servlet이 처리할 때까지 흐름은 다음과 같습니다.
Spring MVC에서 Servlet의 인스턴스는 DispatcherServlet
입니다. 하나의 Servlet은 하나의 요청과 응답을 처리할 수 있지만, 위 그림에서 볼 수 있다싶이 여러 개의 Filter가 하나의 요청과 응답에 사용될 수 있습니다.
Filter의 역할은 두 가지입니다.
- 다운스트림에 존재하는 다른 Filter와 Servlet이 실행되는 것을 방지한다.
- 다운스트림에 존재하는 다른 Filter와 Servlet에서 사용할 요청과 응답을 수정한다.
예를 들어, Filter를 이용하면 사용자가 권한이 없는 경우 Servlet까지 요청을 보내지 않고 빠르게 거절하고 응답을 보낼 수 있습니다. 혹은 사용자 요청의 헤더에서 JWT를 추출하여 역직렬화하여 객체 상태로 HttpServletRequest
에 담아 Servlet에서 사용하도록 수정할 수 있습니다.
Filter는 Spring의 기술이 아니다
Spring Security는 Filter로 동작하지만 Filter의 패키지는 javax.servlet
으로서 Spring의 기술이 아닙니다. 따라서 Spring의 표준으로 구현한 Filter을 Servlet container에 등록하는 것은 불가능합니다. Servlet container는 Servlet만의 표준이 존재하기 때문이죠.
그러나, 우리는 Spring 기반의 Filter를 Spring Bean으로 등록할 수 있고, Servlet container에도 등록할 수 있습니다. 앞서 말한 문제를 해결하기 위해 Spring에서 DelegatingFilterProxy
를 제공하기 때문입니다.
DelegatingFilterProxy
는 Servlet의 Filter 인터페이스로 구현된 Spring Bean에 필터링 작업을 위임하는 프록시입니다. 앞에서 봤던 도식표를 Spring Security의 모습으로 변경하면 다음과 같습니다.
지연로딩 지원
DelegatingFilterProxy
는 Spring Bean을 Servlet container에 등록할 수 있도록 도울 뿐만 아니라 Filter bean이 필요할 때 불러오는 지연 로딩까지 지원합니다. 덕분에 초기 로딩 시간을 줄여 애플리케이션 부팅 시간을 단축시킬 수 있습니다. 다음은 실제 코드에서 지연 로딩에 관련된 부분입니다.
DelegatingFilterProxy와 FilterChainProxy
한편, Spring Security는 FilterChainProxy
라는 특별한 Filter를 제공합니다. 하나의 애플리케이션 내에 FilterChain은 여러 개 존재할 수 있는데, FilterChainProxy는 요청에 맞는 FilterChain에 위임합니다.
FilterChainProxy는 Spring에서 정의한 Filter이기 때문에 DelegatingFilterProxy에 의해 래핑되어집니다. 지금까지 내용을 도식으로 다시 나타내면 다음과 같습니다.
일반적인 Filter가 아닌 FilterChainProxy를 사용함으로써 얻을 수 있는 이점은 두 가지입니다.
- Spring Security의 시작점이 된다.
- 더 많은 조건으로 FilterChain을 선택할 수 있다.
첫 번째로, Spring Security의 모든 시작점은 FilterChainProxy가 되므로 Spring Security에 문제가 발생하여 분석하고싶을 때 디버그의 시작 지점으로 사용하면 쉽게 흐름을 파악할 수 있씁니다.
두 번째로, URL에 의해 실행되는 일반적인 Filter와 달리 HttpServletRequest의 모든 정보를 사용하여 FilterChain을 실행할 수 있습니다. 예를 들어, 헤더에 JWT가 있는 경우에는 JWT에 해당하는 FilterChain을 거치도록 위임할 수 있습니다.
마무리하며
이번 글에서는 Servlet 기반의 Spring Security의 구조에 대해 알아보았습니다. 내용을 요약하면 다음과 같습니다.
- Spring Security는 Servlet의 Filter로 동작한다.
- Servlet 표준의 Filter를 Spring Bean으로 등록하기 위해 DelegatingFilterProxy를 사용한다.
- 여러 개의 FilterChain 사이에서 유연하게 위임하기 위해 FilterChainProxy라는 특별한 Filter를 제공한다.
댓글남기기