Spring Security에서 인증에 대해 알아보자
지난 글에서는 Spring Security의 핵심 동작 원리에 대해 알아보며 전체적인 큰 그림을 이해하였습니다. 이번 글에서는 Spring Security에서 인증을 수행하는 방법에 대해 알아보겠습니다. 참고 자료는 다음과 같습니다.
- Servlet Authentication Architecture :: Spring Security
- Remember-Me Authentication :: Spring Security
Spring Security의 계층 구조
Spring Security의 인증 구조는 계층적으로 이루어져있습니다. 각 계층별 요소는 다음과 같습니다.
- SecurityContextHolder: Spring Security에서 인증된 유저의 정보를 저장하는 곳
- SecurityContext: 현재 유저에 대한 인증 정보(Authentication)
- Authentication: 현재 유저를 나타내는 모델
- Principal: 유저를 식별하는 요소. (e.g. 아이디, 이메일)
- Credentials: Principal이 정확한지 검증하는 요소. (e.g. 비밀번호)
- Authorities: Principal이 가지고 있는 권한
SecurityContextHolder
SecurityContextHolder는 Spring 애플리케이션에서 인증된 유저의 정보를 저장하는 곳입니다. SecurityContextHolder는 내부에 저장된 데이터가 어떻게 저장되었는지는 상관하지 않습니다. 그저 정보가 있다면 해당 유저는 인증되었다고 간주합니다.
SecurityContext의 범위
기본적으로 SpringContextHolder는 인증 정보를 thread-local 단위로 관리합니다. 즉, SecurityContext는 같은 쓰레드 내에서만 접근할 수 있습니다. 덕분에 요청이 끝났을 때 Spring 애플리케이션의 인증 정보가 사라지므로 보안상 안전함을 제공합니다.
그러나 이는 현재 쓰레드에서 새로운 쓰레드를 생성할 경우, 자식 쓰레드는 부모 쓰레드의 SecurityContext를 가지고 있지 않는다는 것을 의미합니다. 따라서 자식 쓰레드에서 부모 쓰레드의 인증 정보가 필요할 경우 매개 변수로 넘겨주거나 SecurityContextHolder의 전략을 다른 것으로 변경해야합니다.
AuthenticationManager
Spring Security에서 인증은 AuthenticationManager에 의해 수행됩니다. AuthenticationManager는 인터페이스로서 인증에 대한 API를 명세하고 있습니다.
ProviderManager과 AuthenticationProvider
다행히 Spring Security에서는 AuthenticationManager의 기본 구현체로 ProviderManager를 제공하고 있습니다. ProviderManager들은 여러 개의 AuthenticationProvider를 보유하고 있습니다.
ProviderManager는 보유한 AuthenticationProvider에 Authentication에 대한 인증 과정을 위임합니다.
앞서 살펴봤던 Filter처럼 AuthenticationProvider는 주어진 Authentication에 대하여 세 가지 경우로 판별합니다.
- 인증 성공
- 인증 실패
- 다운 스트림의 AuthenticationProvider에게 위임
일반적으로 각각의 AuthenticationProvider는 어떤 유형의 Authentication을 처리할 수 있는 지 알고 있습니다.
기본적으로 ProviderManager는 인증이 성공하면 Authentication 객체로부터 Credential과 같이 민감한 정보를 제거합니다.
Spring Security 인증 흐름
Spring Security에서 인증을 담당하는 Filter는 AbstractAuthenticationProcessingFilter
입니다. 이름에서 알 수 있듯 해당 필터는 추상 클래스이며 인증 방법에 따라 구현하여 사용합니다.
AbstractAuthenticationProcessingFilter는 브라우저를 이용한 HTTP 기반 인증 요청에 대해 사용되는 Filter로서 이를 사용했을 때 전체적인 흐름은 다음과 같습니다.
- 유저가 인증 정보를 담아 인증을 요청하면 AbstractAuthenticationProcessFilter는 HttpServletRequest로부터 Authentication 객체를 생성합니다. 이때, Authentication은 인증되지 않은 상태입니다.
- 이후 AbstractAuthenticationProcessFilter에 의해 Authentication 객체에 맞는 AuthenticationManager로 전달됩니다. Authentication을 받은 AuthenticationManager는 인증을 시도합니다.
- 인증에 실패한 경우 다음 작업이 수행됩니다.
- SecurityContextHolder를 비운다.
- RememberMeServices.loginFail이 실행된다. (아래에서 설명)
- AuthenticationFailureHandler가 실행된다.
- 인증에 성공한 경우 다음 작업이 수행됩니다.
- SessionAuthenticationStrategy가 새로운 로그인을 알린다.
- Authentication이 SecurityContextHolder에 저장된다.
- RememberMeServices.loginSuccess가 실행된다. (아래에서 설명)
- ApplicationEventPublisher가 InteractiveAuthenticationSuccessEvent를 발행한다.
- AuthenticationSuccessHandler가 실행된다.
RememberMeService
Remember-Me는 사용자가 로그인한 후 애플리케이션을 종료하거나 세션이 만료되더라도 세션 사이의 로그인 상태를 유지할 수 있도록합니다. 일반적으로 브라우저에 쿠키를 전송하여 이루어지며, 추후의 세션 중 쿠키를 감지하고 자동 로그인합니다.
Spring Security는 RememberMeServices 인터페이스를 정의하여 위 기능을 지원하며, 해시 기반의 구현체과 영구 저장소 기반 구현체를 제공하고 있습니다.
제공하는 메서드는 3가지이며 각 기능은 다음과 같이 실행됩니다.
-
autoLogin(): SecurityContextHolder가 Authentication을 보유하고 있지 않을 때 RememberMeAuthenticationFilter가 실행
-
loginSuccess(): AbstractAuthenticationProcessingFilter에서 인증에 성공했을 때
-
loginFail(): AbstractAuthenticationProcessingFilter에서 인증에 실패했을 때
RememberMeServices는 인증과 관련된 이벤트에 대해 알림을 제공하여 로그인이나 로그아웃 시 필요한 처리를 수행합니다. 또한, 유저가 Remember-Me 기능을 요청하는 경우 쿠키와 같은 인증 정보를 확인하여 적절한 구현체에 위임하는 역할도 수행합니다.
마무리하며
이번 글에서는 Spring Security에서 인증 절차에 대해 알아보았습니다. 글의 내용을 요약하면 다음과 같습니다.
- Spring Security는 계층적 구조로 인증 정보를 관리하며 SecurityContextHolder, SecurityContext, Authentication로 구성한다.
- 인증은 AuthenticationManager에 의해 수행되며 기본 구현체는 ProviderManager이다.
- ProviderManager는 보유한 여러 AuthenticationProvider로 인증을 처리한다.
- AuthenticationProvider는 각자 처리할 수 있는 Authentication이 정해져있다.
- AbstractAuthenticationProcessingFilter는 HTTP 기반 인증 요청을 처리하는 필터이다.
- 인증 성공 시 SecurityContextHolder에 Authentication이 저장되고, 실패 시 보안 조치가 취해진다.
- RememberMeServices는 세션 간 로그인 상태를 유지하는 기능을 제공한다.
댓글남기기