Spring Security 들여다보기 (1)
Spring / Spring Security filter chain 들여다보기Spring 혹은 Spring Security를 사용하면 filter라는 키워드를 종종 듣게 됩니다.단순히 Http 요청이 Spring Application 영역으로 전달되기 전에 Servlet단에서 chain 형식
e4g3r.tistory.com
지난 포스팅에서는 Spring Security Filter Chain의 흐름을 살펴보았습니다.
작성한 포스팅을 다시 읽어보니 인가 과정에 좀 치우쳐져 있는 것 같아 이번에는 인증 과정을 좀 더 살펴보았습니다.
SecurityContextHolder, SecurityContext, Authentication
지난 포스팅에서도 언급 되었던 Spring Security의 핵심 객체들입니다.
공식문서에 따르면 SecurityContextHolder는 Spring Security 인증 모델의 핵심이라고 표현하며, SecurityContextHolder는
인증된 사용자의 정보를 관리하는 역할을 합니다.
결국 Spring Security에서 인증이란 것은 SecurityContextHolder를 통해 사용자의 정보를 저장하는 행위라고 볼 수 있습니다.
추가로 위 그림에서 SecurityContext는 현재 사용자의 보안 Context를 의미하고 이 Context의 요소인
Auhtentication객체에 실제 인증된 사용자의 정보가 담겨져 있습니다.
SecurityContextHolder가 사용하는 전략마다 다르지만 일반적으로는 사용자마다 각각의 SecurityContext를 소유하기 때문에
SecurityContextHolder는 현재 사용자의 SecurityContext를 저장하고 조회할 수 있게 해주는 역할이라고 보면 되겠습니다.
AbstractAuthenticationProcessingFilter
공식문서를 읽어보니 인증 로직 출발점인 AbstractAuthenticationProcessingFilter를 볼 수 있었습니다.
AbstractAuthenticationProcessingFilter는 인증 요청을 가공 한다음 적절한 AutheticationManager에게 위임하는 Filter입니다.
추상 클래스이기 때문에 인증방식에 따라 Filter를 적절하게 구현할 수 있습니다.
예를 들어 formLogin을 통해 인증을 시도하는 경우에는 UsernamePasswordAuthenticationFilter 구현체가 사용됩니다.
해당 구현체는 Request로부터 username과 password를 파싱해서 Authentication을 생성하고 authenticationManager에게
전달하여 인증 처리를 요청합니다.
AuthenticationManager
Filter로부터 인증 요청을 받는 AuthenticationManager를 알아보겠습니다.
공식문서에 따르면 AuthenticationManager는 Filter가 인증을 수행하는 방법을 정의한 인터페이스라고 되어있습니다. AuthenticationManager는 구현 방식에 따라 인증을 처리하고 인증을 완료되었음을 나타내는 Authentication 객체를 반환하며
AuthenticationManager를 호출한 controller / filter가 SecurityContextHolder에 설정하면 된다고 합니다.
AuthenticationManager는 authenticate 선언부가 존재합니다. 공식문서가 말한 것처럼 인증 방식을 정의한다고 볼 수 있습니다.
ProviderManager
AuthenticationManager의 구현체 중 가장 일반적으로 사용되는 것이 ProviderManager 입니다.
ProviderManager는 내부적으로 여러 개의 AuthencationProvider를 가지고 있습니다.
위 그림을 보면 결국 ProviderManager는 여러 개의 AuthenticationProvider를 순회하며 인증을 처리하는 것을 볼 수 있습니다.
위 코드는 ProviderManager의 authenticate 메서드입니다. AuthenicationProvider를 순회하는 것을 볼 수 있습니다.
AuthenticationProvider
이제 마지막 단계인데요. AuthenticationProvider가 인증을 처리하는 실체/주체라고 볼 수 있습니다.
formLogin의 경우는 DaoAuthenticationProvider를 사용하게 되며 만약 JWT 토큰 인증을 처리하고 싶다면
JwtAuthenticationProvider를 직접 구현하여 사용하면 됩니다.
DaoAuthenticationProvider를 확인해보면 AbstractUserDetailsAuthenticationProvider를 구현하고 있습니다.
Spring Security를 사용하면 종종 UserDetails를 접할 수 있는데 일반적으로 id, 비밀번호, 권한, 계정 상태가 담겨있습니다.
그래서 보통 User Entity를 만들 때 UserDetails를 상속해서 만들기도 합니다.
먼저 Filter로부터 받은 Authentication 객체로부터 ID를 가져오고 ID를 통해 UserDetails를 조회합니다.
일반적으로는 데이터베이스로부터 조회하는 로직이 담깁니다.
조회 된 UserDetails를 이용해 여러가지 검증을 합니다. 계정 상태 검사, 비밀번호 검사 등 구현체에 따라 달라질 수 있습니다.
비밀번호를 사용하는 인증의 경우 해시화 되어 저장된 비밀번호와 인증 요청 비밀번호를 비교하는 로직이 수행됩니다.
최종적으로 인증에 성공하면 UserDetails에 남아있는 비밀번호 데이터를 보안을 위해 정리해주고 인증이 완료되었음을 의미하는
Authentication 객체를 반환해줍니다.
지금까지 플로우를 formLogin 상황에 대입해보면
- /api/login 인증 요청 (Request username, password)
- UsernamePasswordAuthenticationFilter(AbstractAuthenticationProcessingFilter)가 Request Parsing 및
Authentication 생성 후 ProviderManager(AuthenticationManager)에게 인증 위임 - ProviderManager는 인증을 처리할 수 있는 AuthenticationProvider를 찾아 인증 처리 후 인증 완료 된 Authentication 반환
위와 같이 정리가 됩니다.
최종적 AbstractAuthenticationProcessingFilter에서는 AuthenticationProvider가 반환한 인증 완료 Authentication 객체를
SecurityContextHolder에 저장하는 로직을 수행합니다.
위 과정들이 Spring Security Authentication Filter를 이용한 인증 처리였습니다.
물론 위 과정들은 Spring Security가 기본적으로 제공하는 구현체들 Spring Security가 기본적으로 제공하는 formLogin이기 때문에
AbstractAuthenticationProcessingFilter -> UsernamePasswordAuthenticationFilter
AuthenticationManager -> ProviderManager
AuthenticationProvider -> DaoAuthenticationProvider
위 구현체들이 알아서 적용되게 됩니다.
따라서 별도의 플로우를 적용하고 싶다면 직접 구현체를 구현하면 됩니다.
Direct Use SecurityContextHolder
하지만 공식문서에서도 언급되어 있는 것처럼 AbstractAuthenticationProcessingFilter, AuthenticationManager, AuthenticationProvider를 사용하지 않고 직접 SecurityContextHolder에 사용할 때가 있는데요.
바로 JWT 토큰을 이용한 인증입니다.
일반적으로 JWT 토큰 인증을 할 때 OncePerRequestFilter를 구현하여 토큰을 검증하고 SecurityContextHolder에 직접 접근하여
Authentication 객체를 설정해줍니다.
처음에 말한 것처럼 Spring Security에서 인증이란 것은 SecurityContextHolder에 인증 객체 SecurityContext를 설정해주는
행위라고 하였으니 굳이 복잡한 과정을 거치지 않아도 인증을 처리할 수 있습니다.
마무리
Spring Security 인증 처리에 대해 알아보았는데 JWT 위주로 쓰다보니 잘 사용하지 않았던 인증 절차를 알아볼 수 있었던 것 같습니다.
시간이 된다면 SecurityContextHolder를 직접 사용하는 방식이 아닌 AbstractAuthenticationProcessingFilter를 통한 JWT 인증도 구현해봐야겠습니다.
'spring' 카테고리의 다른 글
이벤트 재발행 처리하기 - Transactional Outbox Pattern (0) | 2025.04.08 |
---|---|
Spring Security 들여다보기 (1) (0) | 2025.04.02 |
Spring / Spring Security filter chain 들여다보기 (0) | 2025.03.29 |
이벤트가 한번 만 처리 되도록 보장하기 (feat. 멱등성) (0) | 2025.03.19 |
이벤트 발행으로 결제 후처리 하기 (0) | 2025.03.16 |