2021. 11. 25. 21:23ㆍ작업일지
개요
저번 게시글(https://postwithmemory.tistory.com/8)에서는%EC%97%90%EC%84%9C%EB%8A%94) 인증 방식에 대해 간단히 정리하고, 시큐리티를 적용한 이유에 대해서 간단히 살펴 보았다. 그리고, 인증과 인가와 관련된 서비스 로직의 플로우 차트를 간단하게 그려보았다. 이번 게시글에서는 Spring Security의 구조를 간단히 짚어보고, 프레임워크에 플로우 차트를 적용하는 과정을 정리해볼까 한다.
INDEX
- 스프링 시큐리티의 구조
Filters
DelegatingFilterProxy
FilterChainProxy
SecurityFilterChain
Security Filters
- Handling Security Exception
- 인증
스프링 시큐리티의 구조
Filters
스프링 시큐리티를 매우 간단하게 설명해보자면, 보안과 관련된 서블릿 Filter
를 스프링 컨테이너와 연동 시키는 기능을 지원하는 프레임워크라 할 수 있다.
필터의 경우, 서블릿 request/response와 달리, 하나의 컨테이너에 여러 개를 만들어서, 서로 연결 시켜 하나의 체인을 형성할 수 있다. 필터의 코드 구조를 간단하게 예를 들자면 다음과 같을 수 있다.
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
}
주의 해야할 점은 하나의 필터는 다운 스트림에 있는 나머지 필터와 서블릿에 영향을 주기 때문에 필터의 순서는 정말 중요하다.
DelegatingFilterProxy
스프링은 DelegatingFilterProxy
라는 Filter
구현체로 서블릿 컨테이너를 ApplicationContext
와 연결시킨다.
일반적으로 Filter
자체는 서블릿 컨테이너에서 등록하므로, 스프링이 정의하는 빈을 인식하지 못한다. 하지만 DelegatingFilterProxy
을 통해, 필터를 표준 서블릿 컨테이너 매커니즘으로 등록할 수 있으면서, 모든 처리를 Filter
를 구현한 스프링 빈으로 위임해준다.
FilterChainProxy
스프링 시큐리티는 DelegatingFilterProxy
를 감싼 FilterChainProxy
을 통해 서블릿을 지원한다.
SecurityFilterChain
스프링 시큐리티는 FilterChainProxty
를 통해 SecurityFilterChain
에 보안 관련 필터체인을 요청한다. SecurityFilterChain
의 경우, URI에 따라, SecurityFilterChain
을 다르게 분리해서 적용할 수 있다.
Security Filters
보안 필터는 SecurityFilterChain
API를 사용해서 FilterChainProxy
에 추가한다. 당연하게도 필터의 순서는 중요하지만, 일반적으로는 스프링 시큐리티의 Filter
순서를 알아야 할 필요는 없다.
Handling Security Exception
SecurityFilterChain
의 구성요소 중에는 ExceptionTranslationFilter
가 있는데, 이 Filter는 AccessDeniedException
이나, AuthenticationException
을 감지하여, HTTP 응답으로 바꿔준다.
그 외의 Filter들
스프링 시큐리티 자체에서 제공하는 Filter들의 종류는 매우 다양하다. 드문 경우긴 하지만, SecurityFilterChain
API를 활용하여, 보안 필터를 추가할 때, Filter
의 순서를 아는 것이 중요하다. 스프링 시큐리티에서 제공하는 필터의 목록은 다음과 같다.
- 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
인증/인가
스프링 시큐리티는 보안과 관련된 필터 뿐만 아니라, 사용자의 인증(Authentication)과 인가(Authorization)에 대한 api도 제공한다. 인증의 의미와 종류에 대한 설명은 https://postwithmemory.tistory.com/7 게시글을 참고하면 좋겠다.
인증(Authentication)
디테일한 내용은 공식 문서에 자세히 나와있다는 점에서 모든 구성요소를 자세히 이야기 하는 것은 큰 의미가 없을 것 같다. 그래서, 우선은 이해하기 쉽게... 그리고, 프레임워크가 알아서 해주는 부분은 최대한 추상화하고, 개발자가 알아야 하거나, 구현해야하는 부분 위주로, 정리 해보겠다.
우선, 스프링 시큐리티로 인증을 실행할 경우의 흐름을 최대한 간단하게 그림으로 표현 해보았다.
여기에 나온 순서는 외울 필요도 없고, 전부 다 커스터마이징할 생각이 없다면, 구성요소 하나하나를 자세하게 알아야만하는 이유도 없다. 다만 꼭 알고 넘어가야하는 부분은 4 - 8까지의 과정이다. 작동원리야 어쨋든, 로그인시 입력한 인증정보 토큰 값(일반적으로 아이디/비밀번호를 검증받고 시큐리티로부터 받은 일종의 일련번호)은 결과적으로 Manager 로부터 받아와서, Provider 부터 UserDetails 까지 개발자의 입맛에 따라 구성할 줄 알면, 큰 무리는 없을 거라고 본다.
1-10까지 간략하게 설명해보자면...
- AuthenticationFilter에 요청.
- 여기서 말하는 AuthenticationFilter는 위에서 이야기한
SecurityFilterChain
에 해당하는 내용이다. - 일반적인 방식으로 (아이디/비밀번호 입력) 인증을 시도한다면,
UsernamePasswordAuthenticationFilter
를 통해 검증을 시도한다.
- 여기서 말하는 AuthenticationFilter는 위에서 이야기한
- UsernamePasswordAuthenticationToken 발급
- 검증이 통과되면 UsernamePasswordAuthenticationToken을 발급 하는데, 이 토큰은 일종의
Authentication
을 나타낸다. - 이해하기가 어렵다면, 그냥 인증용 객체라고 봐도 무방하다.
- 검증이 통과되면 UsernamePasswordAuthenticationToken을 발급 하는데, 이 토큰은 일종의
- AuthenticationManager
- 일종의 위임 객체로
ProviderManager.authenticate(Authentication authentication)
을 통해,AuthenticationProvider
에 인증 로직을 위임한다. AuthenticationProvider
는 개발자가 직접 인터페이스를 구현하여 사용할 수도 있고, 그냥 프레임워크에서 제공하는AuthenticationProvider
구현체인DaoAuthenticationProvider
를 사용할 수도 있다.- 커스텀
AuthenticationProvider
를 구현하는 방법은 https://www.baeldung.com/spring-security-authentication-provider를 활용해보자. - 그림에서 표현한 4 - 8의 과정은 DaoAuthenticationProvider와 UserDetailsService의 로직 흐름을 그대로 표현하였다. 자세한 내용을 알고 싶으면, api 문서나 혹은 intelliJ와 같은 IDE를 활용하여 소스코드를 직접 추적해보는 것을 추천한다.
- 일종의 위임 객체로
마무리
Security를 적용하는 것 자체가 나한테 꽤 어렵게 받아들여졌다. 알아야 하는 개념도 많아서, 바로 코드로 들어가는데 까지 준비하는 시간이 생각보다 길어져서, 계속 코드를 구현한다기 보다는, 이론을 정리하는 단계가 많아지고 있다.
지금까지는 Security를 적용하기 전에, 인증의 개념을 정리해보고, Spring Security의 구조를 살펴보고 요약을 해보았다. 다음 일지에서는 내가 설계한 로직을 프레임워크에 어떻게 적용하는지에 대한 과정을 정리해보도록 하겠다.
'작업일지' 카테고리의 다른 글
작업 일지#8 테스트 개선 -1- (0) | 2021.12.09 |
---|---|
작업 일지#7 서비스에 Security를 적용해보자 -3- 나의 로직에 시큐리티를 얹어보자 (0) | 2021.11.26 |
작업 일지#5 서비스에 Security를 적용해보자 -1- (0) | 2021.11.14 |
Simple Handling Exception 라이브러리 : 왜 Exception Handling을 처리해야 하는가? -1- (0) | 2021.11.01 |
작업 일지#4 에러 메세지 -1- (0) | 2021.10.29 |