Spring/Spring Security

[Spring Security] 구조(Architecture)와 이해

공대키메라 2022. 7. 16. 11:45

[Spring Security] 구조(Architecture)와 이해

 

지난 시간에 Spring Security의 기본적인 토대에 대해 알아보았다. (궁금하면 여기 클릭!)

 

이번 시간에는 공식 Reference 사이트에서 지난 시간에 이어 Spring Security의 구조에 대해 알아보고자 한다. 

 

참고한 사이트는 다음과 같다.

 

출처 : 

https://docs.spring.io/spring-security/reference/servlet/architecture.html

 

1. 필터 다시보기 (A Reviews of Filters)

스프링 시큐리티의 서플릿 서포트는 Servlet Filter에 기초하며 일반적으로 처음에 Filter의 역할을 보는게 도움이 된다

(Filter를 잘 모르겠으면... 저번에 한 번 정리를 했다! - 끊임없는 깨알 어필!)

 

https://docs.spring.io/spring-security/reference/servlet/architecture.html - Figure1. FilterChain

 

클라이언트가 어플리케이션에 요청을 보내고 URI 요청 경로를 바탕으로 HttpServletRequest를 처리하는 컨테이너(container)나 Filter들과 Servlet을 포함하는 FilterChain을 생성한다. 

 

Spring MVC 어플리케이션에서 Servlet은 DispatcherServlet의 인스턴스다. 많으면 한개의 Servlet이 하나의 HttpServletReuqest 또는 HttpServletResponse를 다룰 수 있다. 하지만, 하나 이상의 fileter가 사용될 수있다.

사용되는 목적은...

 

  • 아래로 흐르는 Filter 혹은 호출된 servlet을 막기 위해서이다. 이 인스턴스에서 Filter는 HttpServgfletResponse를 전형적으로 사용할 것이다. 
  • 아래로 흐르는 Filter와  Sevlet에 의해 사용된 HttpServletRequest와 HttpServletResponse를 수정하기 위해서이다. 
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {
	// 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가 오직 후속 필터와 서플릿에 영향을 주기 때문에, 각 Filter의 순서는 극도로 중요한 상태에서 호출된다. 

 

=> 그러니까 Filter가 앞에서 뒤로 나열되어 있으면, 이 순서에 따라서 Filter가 작동한다. 즉, 이것을 후속 필터에 영향을 준다고 표현하고 있다. (only impacts downstream! 이라고 말이다) 그래서 Filter를 하는 순서가 매우 중요하다는 것이다. 

2. DelegatingFilterProxy

https://docs.spring.io/spring-security/reference/servlet/architecture.html - Figure 2. DelegatingFilterProxy

Spring은 DelegatingFilterProxy라는 Filter 구현체를 제공하는데, 이것은 Servlet 컨테이너의 생명주기와 스프링의 ApplicationContext사이를 이어주는 역할을 한다. 

 

Servlet 콘테이너는 스스로의 기준을 사용해서 Filter 등록을 허용한다. 하지만 스프링에서 정의된 빈들을 모른다. 

 

DelegatingFilterProxy는 표준 Servlet container 메커니즘을 통해 등록될 수 있지만, Filter를 implement하는 스프링 빈의 모든 일을 위임한다.

 

=>  이것을 이름과 설명을 같이 보면 이제 이해가 좀 가는것 같다. Filter의 기능들을 사용할 수 있도록, 대리의 역할을 하는걸 통해(Proxy) 위임한다! 해서 Delegatinn Filter Proxy인 것 같다. 뭘 근거로?

여기 Servlet 컨테이너는 Filter를 등록할 수는 있는데 Spring bean을 모른다.

왜냐? 기본적으로 Filter는 spring 에서 제공하는 기능이 아닌 Servlet에서 제공하는 기능이기 때문이다. 여기서 bean을 받아서 사용할 수 있는 DelegatingFilterProxy를 생성해서 Spring내에 정의된 bean에 접근할 수 있도록 한다는 것이다. 

Proxy는 대리자로, Filter를 대리인으로 Spring Bean을 사용할 수 있도록 위임한다는 것이다. 

내말이 틀리다면 언제든지 비난의 화살 날려주세요 퓨퓨퓽~!

3. FilterChainProxy

https://docs.spring.io/spring-security/reference/servlet/architecture.html - Figure 3. FilterChainProxy

스프링 시큐리티의 서블릿 지원은 FilterChainProxy내에 포함되어있다.

 

FilterChainProxy는 SecurityFilterChain을 통해 많은 Filter 인스턴스들을 위임하도록 해주는 Spring Security 에 의해 제공되는 특별한 Filter다. 

 

FilterChainProxy가 빈이기 때문에, 전형적으로 DelegatingFilterProxy안에 있다. 

 

=> 바로 위해서 DelegatingFilterProxy가 무슨 역할을 했지? Filter기능을 사용할 수 있도록 하는 대리자로서 기능을 위임하는 역할을 한다고 했다.

그런데 이런!

FilterChainProxy가 우리가 Security기능을 사용하려는 많은 기능들을 가지고 있다고 한다.

근데 공교롭게도 bean이라네?

그렇다는 말은 Servlet container안에 있는 Filter와 접근이 불가능하다는 말이다.

그래서 DelegatingFilterProxy를 이용하는 것이다! spring Bean에 접근하기 위해!

4. SecurityFilterChain

SecurityFilterChain은 FilterChainProxy에 의해 사용되는데 Spring Security Filter가 요청에 의해 호출된 것을 결정하기 위해 사용된다. 

 

https://docs.spring.io/spring-security/reference/servlet/architecture.html - Figure 4. SecurityFilterChain

 

SecurityFilterChain 안에 Security Filter들은 전형적으로 빈(Bean)들이다. 하지만, DelegatingFilterProxy를 대신해서 FilterChainProxy와 함께 등록된다. 

 

FilterChainProxy는 Servlet 컨테이너 혹은 DelegatingFilterProxy를 직접적으로 등록함으로서 많은 이점을 제공한다. 

 

첫째로, 스프링 시큐리티의 서플릿 지원의 모두에게 시작점을 제공한다. 

 

그러한 이유로, 스프링 시큐리티의 서블릿 지원 문제를 해결하려 한다면, FilterChainProxy에 디버그 시점을 추가하는게 시작하기 좋은 곳이다. 

 

둘째로, FilterChainProxy가 스프링 시큐리티 사용의 핵심이기에, optional하게 보이지 않는 작업을 수행할 수 있다. 예를 들어, 메모리 누수를 방지하기 위해 SecurityContext를 지울 수 있다. 

 

또한, 특정 공격 타입을 막아주는 HttpFirewall을 적용할 수 있다. 

 

게다가,  SecurityFilterChain이 호출될 때 더 많은 유연성을 제공한다. 

 

하지만, FilterChainProxy는 RequestMatcher 인터페이스에 기능을 더 발전시켜서 HttpServletRequest에서 어떠한 것을 바탕으로 호출할지 결정할 수 있다. 

 

https://docs.spring.io/spring-security/reference/servlet/architecture.html - Figure 5. Multiple SecurityFilterChain

 

Multiple SecurityFiltetChain 그림에서 FilterChainProxy가 어떤 SecuriyFilterChain 이 사용되어야 하는지 결정한다.

 

URL이 만약 "/api/messages"이 요청된다면, 알맞은 SecurityFilterChain의 패턴에 맞는것을 매치할 것이다. 

 

=> URL에 맞춰서 원하는 Filter를 타도록 결정이 가능하다는 것이다.

여기까지 크게 정리를 해보자.

스프링 시큐리티에서 제공하는 Security Filter들의 묶음을 SecurityFilterChain이라고 볼 수 있다. 

이러한 Chain의 덩어리들은 Security Filter들이 빈으로 등록되어 있는 곳인데, FilterChainProxy에서 Sevlet에서도 이 Bean을 인식할 수 있도록 DelegatingFilterProxy를 제공한다. 

5. Security Filters

시큐리티 필터(Security Filters)는 SecurityFilterChain API와 함께 FilterChainProxy에 있다.

 

Filter의 순서는 중요하다. 전형적으로 Spring Security의 filter들의 순서를 알 필요는 없다. 하지만, 순서를 아는 것에 몇개의 장점이 있다.

 

다음이 스프링 시큐리티 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

진짜 캐많네 ㅋㅋ

6. Handling Security Exceptions

ExceptionTanslationFilter는 AccessDeniedException과 AuthenticationException을 HTTP 응답으로 변형되도록 돕는다. 

 

ExceptionTranslationFilter 는 Security Filter중 하나인 FilterChainProxy에 놓인다. 

 

https://docs.spring.io/spring-security/reference/_images/servlet/architecture/exceptiontranslationfilter.png

 

그림의 각 번호에 따라 작동이 어떻게 되는지 알아보자. 

 

1. ExceptionTanslationFilter는 다른 어플리케이션에서 나머지 Filter들을 호출하기 위해 FIlterChain.doFilter(request, response)를 호출한다

=> 다시 말하면, 여러개의 Filter가 있는데, 그 필터들이 downstream하는 하향식으로 한 방향으로 작동하는데, 뒤에 있는 Filter들을 호출하는 것을 나머지하고 본 글에서 표현하고 있다.(invoke the rest of the application)

 

2. 만약 사용자가 인증되지 않았거나 인증 오류(AuthenticationException)라면,  Authentication(인증)을 시작한다.(인증이 안된 상태라면 인증을 해서 이게 인가 여부를 판단해야한다!)

 

  • SecurityContextHander가 청소된다 (지워진다고 보면 될 것 같다.)
  • HttpServletRequest가 RequestCashe에 저장된다. 사용자가 성공적으로 인증할 떄, RequestCache가 원래 요청을 다시 시도하는데 사용된다. 
  • AuthenticationEntryPoint가 client로 자격을 요청하기 위해 사용된다. 예를 들어, WWW-Authentication 헤더를 보내거나 페이지에 log를 리다이렉트할 수도 있다. 

 

3. AccessDeniedException이라면, Access Deined(접근이 제한된다!) AccessDeniedHandler가 접근 제한을 다루기 위해 호출된다. 

 

만약 어플리케이션이 AccessDeniedException이나 AuthenticationException을 던지지 않으면, ExceptionTranslationFilter는 아무것도 하지 않는다.

 

이것을 이제 공식문서에서 어떻게 코드로 처리했는지 간단하게 적어두었다.

 

ExceptionTranslationFilter pseudocode

try {
	filterChain.doFilter(request, response);
} catch (AccessDeniedException | AuthenticationException ex) {
	if (!authenticated || ex instanceof AuthenticationException) {
		startAuthentication();
	} else {
		accessDenied();
	}
}

이번 시간에는 Spring Security의 Architecture에 대해서 알아보았다. 

 

정리를 하자면, 여러개의 Filter가 있는데, 그것을 스프링 시큐리티용 Filter를 장착해서 우리가 맞는 설정에 따라서 돌아가도록 한다는 것이다. 

 

다음에는 Authentication (인증)에 대해 더 알아볼 것이다.