티스토리 뷰
스프링 문서를 참고하여 정리하였습니다.
Architecture :: Spring Security
Spring Security’s Servlet support is based on Servlet Filters, so it is helpful to look at the role of Filters generally first. The picture below shows the typical layering of the handlers for a single HTTP request. The client sends a request to the appl
docs.spring.io
이 포스팅에서는 서블릿 기반 애플리케이션 내에서 Spring Security의 아키텍처에 대해 다룬다.
Filter
Spring Security의 Servlet 지원은 Servlet 필터를 기반으로 하기 때문에 필터의 역할을 먼저 살펴보는 것이 도움이 된다.
아래 그림은 단일 HTTP request에 대한 handler의 일반적인 계층화를 보여준다.
Client는 Application에 request을 보내고 컨테이너는 request URI path를 기반으로 HttpServlet request을 처리해야 하는 Filter 및 Servlet을 포함하는 FilterChain을 만든다.
참고로, Spring MVC application에서 servlet은 DispatcherServlet의 인스턴스이다.
최대 하나의 Servlet이 하나의 HttpServletRequest 및 HttpServletResponse를 처리할 수 있다.
그러나 다음과 작업에는 둘 이상의 Filter를 사용할 수 있다.
- 다운스트림 Filter 또는 Servlet이 호출되지 않도록 할 때.
- 위와 같은 경우 Filter는 일반적으로 HttpServletResponse를 쓴다.
- 다운스트림 Filter 또는 Servlet에서 사용하는 HttpServletRequest 또는 HttpServletResponse 수정할 때
Filter의 최대 장점은 다른 Filter로 전달할 수 있는 FilterChain이 있다는 점이다.
fun doFilter(request: ServletRequest, response: ServletResponse, chain: FilterChain) {
// do something before the rest of the application
chain.doFilter(request, response) // invoke the rest of the application
// do something after the rest of the application
}
필터는 다운스트림 Filter 또는 Servlet에만 영향을 미치기 때문에 각 filter가 호출되는 순서는 매우 중요하다.
DelegatingFilterProxy
Spring은 서블릿 컨테이너의 라이프 사이클과 Spring의 Application Context를 브리징할 수 있는 DeglatingFilterProxy라는 필터 구현체를 제공한다.
DelegatingFilterProxy는 표준 서블릿 컨테이너 메커니즘을 통해 등록할 수 있지만, 모든 작업은 필터를 구현하는 Spring Bean에 위임한다.
다음 그림은 DelegatingFilterProxy가 필터 및 필터 체인에 얼마나 적합한지 보여 준다.
DelegatingFilterProxy는 ApplicationContext에서 Bean Filter0을 조회한 뒤, Bean Filter0을 호출한다.
DelegatingFilterProxy의 예시 코드는 다음과 같다.
fun doFilter(request: ServletRequest, response: ServletResponse, chain: FilterChain) {
// Bean Filter0의 인스턴스
val delegate: Filter = getFilterBean(someBeanName)
// delegate work to the Spring Bean
delegate.doFilter(request, response)
}
DelegatingFilterProxy의 또 다른 이점은 필터 빈 인스턴스 검색을 지연시킬 수 있다는 것이다.
이는 컨테이너가 Filter 인스턴스를 등록해야 컨테이너를 시작할 수 있기 때문에 중요하다.
그러나 Spring은 일반적으로 ContextLoaderListener를 사용하여 Filter 인스턴스를 등록해야 할 때까지 수행되지 않는 Spring Bean을 로드한다.
FilterChainProxy
Spring Security은 FilterChainProxy 역시 지원한다.
FilterChainProxy는 SecurityFilterChain을 통해 많은 Filter 인스턴스에 위임할 수 있도록 하는 Spring Security에서 제공하는 특수 필터다.
FilterChainProxy는 Bean이므로 일반적으로 DelegatingFilterProxy에 래핑된다.
SecurityFilterChain
SecurityFilterChain은 FilterChainProxy에서 이 request에 대해 호출할 Spring Security 필터를 결정하는 데 사용된다.
SecurityFilterChain 내의 Security Filter는 일반적으로 Bean이지만 DelegatingFilterProxy 대신 FilterChainProxy에 등록된다.
FilterChainProxy는 Servlet 컨테이너 또는 DelegatingFilterProxy에 직접 등록할 때 여러 가지 장점을 제공한다.
장점 [1]. 스프링 시큐리티의 서블릿 지원의 모든 starting point를 제공한다.
따라서 Spring Security의 Servlet 지원 문제를 해결하려는 경우 FilterChainProxy에서 디버그 지점을 추가하는 것이 좋다.
장점 [2]. FilterChainProxy는 Spring Security 사용의 중심이기 때문에 옵션으로 보이지 않는 작업을 수행할 수 있다.
예를 들어 메모리 누수를 방지하기 위해 보안 컨텍스트를 지운다.
또한 Spring Security의 Http Firewall을 적용하여 특정 유형의 공격으로부터 applicatiob을 보호한다.
장점 [3]. SecurityFilterChain을 호출해야 하는 시점을 보다 유연하게 결정할 수 있다.
서블릿 컨테이너에서 필터는 URL만 기준으로 호출된다.
그러나 FilterChainProxy는 RequestMatcher 인터페이스를 활용하여 HttpServletRequest의 모든 것을 기반으로 호출을 결정할 수 있다.
실제로 FilterChainProxy를 사용하여 사용할 SecurityFilterChain을 결정할 수 있다.
이렇게 하면 application의 여러 슬라이스에 대해 완전히 별도의 구성을 제공할 수 있다.
위 Multiple SecurityFilterChain 그림에서 FilterChainProxy는 사용할 SecurityFilterChain을 결정한다.
일치하는 첫 번째 SecurityFilterChain만 호출된다.
예를 들어 /api/messages/의 URL이 요청되면 먼저 SecurityFilterChain0의 패턴인 /api/**과 일치하므로 SecurityFilterChain 0만 호출된다.
/messages/의 URL이 요청되면 SecurityFilterChain 0의 /api/** 패턴과 일치하지 않으므로 FilterChainProxy는 각 SecurityFilterChain을 계속 시도한다.
다른 인스턴스가 없는 경우 SecurityFilterChain과 일치하는 SecurityFilterChain 인스턴스가 호출된다.
각 SecurityFilterChain은 고유하고 독립적으로 구성할 수 있습니다.
실제로 애플리케이션이 Spring Security에서 특정 요청을 무시하도록 하려면 SecurityFilterChain에 security filter가 0일 수 있다.
security filter는 SecurityFilterChain API를 사용하여 FilterChainProxy에 삽입된다.
필터의 순서는 중요하다.
일반적으로 스프링 security filter의 순서를 알 필요는 없으나 순서를 알면 유익할 때도 있다.
다음은 스프링 security filter의 일반적인 순서다.
- ForceEagerSessionCreationFilter
- ChannelProcessingFilter
- WebAsyncManagerIntegrationFilter
- SecurityContextPersistenceFilter
- HeaderWriterFilter
- CorsFilter
- CsrfFilter
- LogoutFilter
- OAuth2AuthorizationRequestRedirectFilter
- Saml2WebSsoAuthenticationRequestFilter
- X509AuthenticationFilter
- AbstractPreAuthenticatedProcessingFilter
- CasAuthenticationFilter
- OAuth2LoginAuthenticationFilter
- Saml2WebSsoAuthenticationFilter
- UsernamePasswordAuthenticationFilter
- OpenIDAuthenticationFilter
- DefaultLoginPageGeneratingFilter
- DefaultLogoutPageGeneratingFilter
- ConcurrentSessionFilter
- DigestAuthenticationFilter
- BearerTokenAuthenticationFilter
- BasicAuthenticationFilter
- RequestCacheAwareFilter
- SecurityContextHolderAwareRequestFilter
- JaasApiIntegrationFilter
- RememberMeAuthenticationFilter
- AnonymousAuthenticationFilter
- OAuth2AuthorizationCodeGrantFilter
- SessionManagementFilter
- ExceptionTranslationFilter
- FilterSecurityInterceptor
- SwitchUserFilter
Handling Security Exceptions
ExceptionTranslationFilter는 AccessDeniedExcetpion 및 HTTP 응답에 대한 AuthenticationException의 변환을 허용한다.
ExceptionTranslationFilter는 Security Filter 중 하나로 FilterChainProxy에 포함된다.
- 먼저 ExceptionTranslationFilter는 FilterChain.doFilter(request, response)를 호출하여 application의 나머지 부분을 호출한다.
- 사용자가 인증되지 않았거나 AuthenticationException인 경우 Start Authentication으로 진행한다.
- 기존에 있던 SecurityContextHolder는 삭제된다.
- HttpServletRequest는 RequestCache에 저장된다.
- 사용자가 성공적으로 인증되면 RequestCache를 사용하여 원래 요청을 재실행한다.
- AuthenticationEntryPoint는 클라이언트에서 자격 증명을 요청하는 데 사용된다.
- 예를 들어 로그인 페이지로 리디렉션하거나 WWW-Authenticate 헤더를 보낼 수 있다.
- 그렇지 않은 경우 AccessDeniedException가 발생한 다음 액세스가 거부된다.
ExceptionTranslationFilter의 pseudocode는 다음과 같다.
try {
filterChain.doFilter(request, response); // [1]
} catch (AccessDeniedException | AuthenticationException ex) {
if (!authenticated || ex instanceof AuthenticationException) {
startAuthentication(); // [2]
} else {
accessDenied(); // [3]
}
}
- FilterChain.doFilter(request, response)를 호출하는 것이 application의 나머지 부분을 호출하는 것과 동일하다는 것을 알 수 있다. 즉, application의 다른 부분(예: FilterSecurityInterceptor 또는 method security)가 AuthenticationException 또는 AccessDeniedException을 발생하여 catch문으로 향하게 된다.
- 사용자가 인증되지 않았거나 AuthenticationException인 경우 startAuthentication()이 실행된다.
- 그렇지 않으면 액세스가 거부된다.
'Sping Framework > Spring Security' 카테고리의 다른 글
스프링 시큐리티 [4]. Servlet Authentication Architecture (0) | 2022.08.20 |
---|---|
스프링 시큐리티 [3]. Authentication (0) | 2022.08.05 |
스프링 시큐리티 [1]. 자동 설정과 기능 프리뷰 (0) | 2022.07.31 |
- Total
- Today
- Yesterday
- MSA
- 프로그래머스
- AWS
- 알고리즘
- node.js
- 코테
- Kotlin
- Algorithm
- 정규표현식
- 객체지향
- Spring
- 이펙티브 자바
- C++
- 디자인패턴
- 테라폼
- BAEKJOON
- kotest
- Spring Boot
- 디자인 패턴
- BOJ
- 이팩티브 자바
- 클린 아키텍처
- kkoon9
- Olympiad
- Effective Java
- 클린 코드
- 백준
- JPA
- programmers
- Java
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | ||||||
2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 |