일러두기
- 본 글은 스프링 공식 페이지의 Architecture 절을 한국어로 번역한 자료입니다.
- 전문적인 교육을 받은 번역가가 번역한 글이 아니기 때문에 다소의 번역 실수가 있을 수 있습니다. (아마 많을..)
- 해당 기능에 대해 전문적인 지식을 갖춘 엔지니어가 아니라, 스프링 시큐리티를 처음 공부하는 학생이 작성한 글이기 때문에 번역 간의 다수의 오역과 의역이 있을 가능성이 있습니다.
- 결론 : 이 문서는 신뢰할 수 없을 가능성이 높습니다.
- 번역이 매끄럽지 못하다고 판단되는 부분은 파란색으로 원본 문장을 첨부하여 원본을 확인할 수 있도록 하였습니다.
원본 출처
https://docs.spring.io/spring-security/reference/servlet/architecture.html
번역문서
Architecture
이번 절은 서블릿 기반 애플리케이션 내에서 스프링 시큐리티의 상위 수준의 아키텍처(high level architecture)에 대해 논합니다. 이 절은 인증(Authentication), 인가(Authorization), Protection Against Exploits의 절들을 참조, 그에 대한 이해를 바탕으로 작성되었습니다.
필터에 대한 복습 (A Review of Filters)
서블릿에 대한 스프링 시큐리티의 지원은 서블릿 필터에 기반을 두고 있기 때문에, 필터의 역할에 대해 먼저 간략하게 살펴보도록 하겠습니다. (번역가 주 : 의역했다 - Spring Security’s Servlet support is based on Servlet Filters, so it is helpful to look at the role of Filters generally first. : 먼저 필터의 역할에 대해 개괄적으로 살펴보는 것이 도움이 됩니다.) 아래의 그림은 단일 HTTP 요청에 대한 핸들러의 계층을 보여주는 전형적인 예입니다.
클라이언트가 애플리케이션에 요청 메시지를 보내면 컨테이너는 요청 URI의 경로를 기반으로 HttpServletRequest를 처리해야 하는 필터와 서블릿을 포함한 FilterChain을 생성합니다. 스프링 MVC 애플리케이션의 경우 서블릿은 DispatcherServlet의 객체에 해당합니다. 단일 서블릿은 대부분 한개의 HttpServletRequest와 HttpServletResponse만을 처리 할 수 있지만, 두 개 이상의 필터는 다음과 같은 방식으로도 사용할 수 있습니다.
- 하향 스트림(downstream) 필터 혹은 서블릿이 호출되는 것을 막습니다. 이러한 경우, 클라이언트로 반송되는 HttpServletResponse는 일반적으로 필터에 의해 작성됩니다.
- 하향 스트림 필터 혹은 서블릿이 사용하는 HttpServletRequest 혹은 HttpServletResponse를 수정합니다.
필터의 힘은 필터 내에 내장된 FilterChain으로부터 발휘됩니다. The power of the Filter comes from the FilterChain that is passed into it.
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {
// 애플리케이션의 나머지 부분 이전에 실행되는 영역
chain.doFilter(request, response); // invoke the rest of the application
// 애플리케이션의 나머지 부분 이후에 실행되는 영역
}
필터는 오직 하향 스트림 필터와 서블릿에만 영향을 미치기 때문에, 각 필터가 호출되는 순서는 매우(extreamely) 중요합니다.
DelegatingFilterProxy
스프링은 서블릿 컨테이너의 생명주기와 스프링의 ApplicationContext 사이를 이어주는 DelegatingFilterProxy라는 이름의 필터 구현체를 제공합니다. 서블릿 컨테이너는 자체 표준에 의해 필터를 등록하는데, 스프링에 의해 정의된 빈들은 인식하지 못합니다. DelegatingFilterProxy는 서블릿 표준 메커니즘에 의해 등록될 수 있지만 필터를 구현하는 모든 작업을 스프링 빈에 위임합니다.
아래의 그림은 DelegatingFilterProxy가 어떻게 필터와 필터체인에 적용되는지를 보여줍니다.
DelegatingFilterProxy는 ApplicationContext에서 빈 필터0를 찾은 후 해당 빈 필터를 호출합니다. 아래의 코드는 DelegatingFilterProxy를 표현한 가상의 코드입니다.
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {
// 스프링 빈으로 등록된 필터를 지연 획득.
// DelecatingFilterProxy의 경우 delegate는 필터0의 객체에 해당한다.
Filter delegate = getFilterBean(someBeanName);
// delegate는 스프링 빈에 작업을 위임한다.
delegate.doFilter(request, response);
}
DelegatingFilterProxy를 사용하면 얻을 수 있는 또다른 이점은 필터 빈 객체를 지연 획득할 수 있다는 것입니다. Another benefit of DelegatingFilterProxy is that it allows delaying looking Filter bean instances. 이는 컨테이너가 시작되기 전 필터 객체가 컨테이너에 등록될 필요가 있기 때문에 중요합니다. 그러나 스프링은 일반적으로 스프링 빈을 등록하기 위해 필터 객체가 등록되는 시점 이후에 사용 가능한 ContextLoaderListner을 사용합니다. However, Spring typically uses a ContextLoaderListener to load the Spring Beans which will not be done until after the Filter instances need to be registered.
FilterChainProxy
스프링 시큐리티가 제공하는 서블릿에 대한 지원은 FilterChainProxy내에 포함되어 있습니다. FilterChainProxy는 스프링 시큐리티에서 제공하는 특수한 필터로 SecurityFilterChain을 통해 많은 필터 인스턴스에 역할을 위임할 수 있습니다. FilterChainProxy는 빈이기 때문에 주로 DelegatingFilterProxy에 의해 wrapping 됩니다.
SecurityFilterChain
SecurityFilterChain은 특정 요청에 대해 어떤 스프링 시큐리티 필터들이 사용되어야 하는지를 결정하기 위해 사용되며 FilterChainProxy가 사용합니다.
SecurityFilterChain 안에서의 시큐리티 필터들은 일반적으로는 빈이지만, DelegatingFilterProxy가 아닌 FilterChainProxy에 의해 등록됩니다. FilterChainProxy는 서블릿 컨테이너나 DelegatingFilterProxy에 직접 등록할 수 있는 여러 가지의 장점을 제공합니다. 첫 번째, FilterChainProxy는 스프링 시큐리티의 모든 서블릿 지원을 위한 출발점을 제공합니다. 이러한 이유로, 만약 당신이 스프링 시큐리티의 서블릿 지원 문제를 해결해야 한다면, FilterChainProxy에 디버그 포인트를 추가하는 것은 매우 좋은 출발선이 될 것입니다.
두 번째, FilterChainProxy는 스프링 시큐리티 사용의 핵심이기 때문에 선택사항으로 간주되지 않는 작업을 수행할 수 있습니다. 예를 들어, FilterChainProxy는 메모리 누수를 방지하기 위해 SecurityContext를 지울 수 있습니다. For example, it clears out the SecurityContext to avoid memory leaks. 또한 FilterChainProxy는 특정 유형의 공격에 대해 스프링 시큐리티의 HttpFirewall을 적용하여 애플리케이션을 보호할 수 있습니다.
추가적으로 FilterChainProxy는 SecurityFilterChain이 호출되어야 하는 시점을 결정하는 데 더 많은 유연성을 제공합니다. 서블릿 컨테이너 내에서의 필터는 URL만을 기반으로 호출됩니다. 그러나 FilterChainProxy는 RequestMatcher 인터페이스를 활용하여 HttpServletRequest의 모든 것을 기반으로 호출을 결정할 수 있습니다.
사실, FilterChainProxy는 사용해야 할 SecurityFilterChain을 결정할 수 있습니다. 이렇게 하면 애플리케이션의 여러 슬라이스(slices)에 대한 완전히 독립적인 구성을 제공할 수 있습니다. This allows providing a totally separate configuration for different slices of your application.
다수의 SecurityFilterChain 환경에서 FilterChainProxy는 어떤 SecurityFilterChain이 사용되어야 하는지 결정합니다. In the Multiple SecurityFilterChain Figure FilterChainProxy decides which SecurityFilterChain should be used. 일치하는 첫 번째 SecutiryFilterChain만 호출됩니다. 만약 /api/messages/ 에 해당하는 URL이 요청되면, SecurityFilterChain0의 /api/** 패턴과 가장 처음 일치될 것입니다. 따라서 비록 SecurityFilterChainn과 패턴이 일치한다 하더라도, SecurityFilterChain0만이 호출될 것입니다. 만약 /messages/ 에 해당하는 URL이 요청되면, SecurityFilterChain0만 /api/**과 일치하지 않기 때문에, FilterChainProxy는 각각의 SecurityFilterChain에 접근을 수행할 것입니다. 만약 일치하는 다른 조건이 없다면, SecurityFilterChainn과 패턴이 일치하는 SecurityFilterChain 객체가 호출될 것입니다.
SecurityFilterChain0에는 오직 세개의 시큐리티 필터 인스턴스만이 설정되어 있다는 부분에 주목해 주십시오. SecurityFilterChainn에는 4개의 시큐리티 필터들이 설정되어 있습니다. 각각의 SecurityFilterChain은 고유하며 설정환경은 독립되어 있다는 사실을 언급하는 것은 중요합니다. It is important to note that each SecurityFilterChain can be unique and configured in isolation. 만약 스프링 시큐리티가 특정 요청을 무시하기를 애플리케이션에서 원한다면 SecurityFilterChain에 0개의 시큐리티 필터를 적용하는것도 가능합니다.
Security Filters
시큐리티 필터는 SecurityFilterChain API를 이용하여 FilterChainProxy 내에 삽입됩니다. 필터의 순서가 중요합니다. 스프링 시큐리티 필터의 순서를 아는 것이 일반적으로 필수적인 것은 아니지만, 순서를 아는 것이 분명 유익할 때가 있습니다.
아래는 스프링 시큐리티 필터의 종합적인 순서를 보여줍니다.
- 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
시큐리티 예외 핸들링
ExceptionTranslationFilter은 AccessDeniedException과 HTTP 응답 내에서의 AuthenticationException을 변환(translation)하는 역할을 맡습니다.
ExceptionTranslationFilter은 Security Filters 중 하나로 FilterChainProxy 내에 삽입됩니다.
- 첫 번째, ExceptionTranslationFilter가 나머지 애플리케이션의 호출을 위해 FilterCahin.doFilter(reqeust, response)를 호출합니다.
- 인증된 사용자가 아니거나 AuthenticationException이 발생할 경우, 인증 작업을 시작합니다.
- SecurityContextHolder가 제거됩니다.
- HttpServletRequest는 RequestCache에 저장됩니다. 사용자의 인증이 성공적으로 수행되면 RequestChache는 원래의 요청을 재생하는데 사용됩니다.
- AuthenticationEntryPoint는 클라이언트에 자격 증명을 요청하는 데 사용됩니다. 예를 들어, 로그인 페이지로의 리다이렉트를 수행할 수도 있고 WWW-Authenticate 헤더를 전송할 수도 있습니다.
- 이외로 AccessDeniedException이 발생할 경우, 접근은 거부됩니다. AccessDeniedHandler가 요청을 거부하기 위해 호출됩니다.
Note
만약 애플리케이션이 AccessDeniedException 혹은 AuthenticationException을 던지지 않는 경우, ExceptionTranslationFilter은 더 이상 아무런 작업을 하지 않습니다.
ExceptionTranslationFilter에 대한 가상의 유사 코드는 다음과 같습니다.
try {
filterChain.doFilter(request, response);
} catch (AccessDeniedException | AuthenticationException ex) {
if (!authenticated || ex instanceof AuthenticationException) {
startAuthentication();
} else {
accessDenied();
}
}
- A Review of Filters 파트에서 FilterChain.doFilter(request, response)를 호출하는 것이 나머지 애플리케이션을 호출하는 것과 같다는 것을 배웠다는 사실을 떠올리십시오. You will recall from A Review of Filters that invoking FilterChain.doFilter(request, response) is the equivalent of invoking the rest of the application. 이 말의 의미는 즉, 만약 애플리케이션의 또 다른 부분(예를 들자면 FilterSecurityInterceptor 또는 메소드 시큐리티)이 AuthenticationException 혹은 AccessDeniedException을 던질 경우, 그 예외가 이 곳으로 돌아와 처리된다는 것을 의미합니다.
- 인증된 사용자가 아닌 경우 혹은 AuthenticationException이 발생한 경우, 인증작업을 시작합니다.
- 다른 모든 경우, 접근이 거부됩니다.
'Spring Framework > 스프링 시큐리티' 카테고리의 다른 글
[스프링 시큐리티 무작정 따라하기] 2. 회원 관리하기 (2) | 2022.07.31 |
---|---|
[스프링 시큐리티 무작정 따라하기] 1. Hello Spring Security (0) | 2022.07.12 |
[스프링 시큐리티 - 인증] 공식문서 번역하며 공부하기 - 서블릿 인증 아키텍처 (0) | 2022.01.11 |
[스프링 시큐리티 - 인증] 공식문서 번역하며 공부하기 - Authentication (0) | 2022.01.07 |
[스프링 시큐리티] 공식문서 번역하며 공부하기 - Hello Spring Security (0) | 2022.01.02 |