[Spring Security] 인증 아키텍처
🔐 스프링 시큐리티
👽 Authentication
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package org.springframework.security.core;
import java.io.Serializable;
import java.security.Principal;
import java.util.Collection;
public interface Authentication extends Principal, Serializable {
Collection<? extends GrantedAuthority> getAuthorities();
Object getCredentials();
Object getDetails();
Object getPrincipal();
boolean isAuthenticated();
void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException;
}
principal: 인증 주체를 의미하며 인증 요청의 경우 사용자 이름(아이디)을, 인증 후에는UserDetails타입의 객체가 될 수 있다.credentials: 인증 주체가 올바른 것을 증명하는 자격 증명으로서 대개 비밀번호를 의미한다.
🍕 SecurityContextHolder

SecurityContextHolder 는 현재 인증된 사용자의 정보를 SecurityContext 객체에 저장하고 있다. 인증 주체(principal)에 대한 정보를 얻으려면 SecurityContextHolder 에 접근하면 된다. 아래는 SecurityContextHolder 를 통해 인증 정보를 조회하는 방법이다.
1
2
3
4
5
SecurityContext context = SecurityContextHolder.getContext();
Authentication authentication = context.getAuthentication();
String username = authentication.getName();
Object principal = authentication.getPrincipal();
Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
기본적으로 SecurityContextHolder 는 ThreadLocal 을 사용해 보안 컨텍스트를 저장한다. ThreadLocal 을 사용하면 각 스레드가 독립적인 SecurityContext 를 가지게 되며, 동일한 스레드 내의 메서드에서 SecurityContext 에 접근할 수 있다.
그러나 ThreadLocal 은 스레드가 종료되면 자동으로 정리되지 않으므로 애플리케이션이 스레드를 재사용하거나 스레드 풀이 있는 환경에서는 주의를 기울여야 한다. FilterChainProxy 는 SecurityContext 가 항상 적절히 지워지도록 보장해 이러한 문제를 방지한다.
SecurityContextHolder 는 SecurityContext 를 저장하는 다양한 전략을 제공한다. SecurityContextHolder.MODE_THREADLOCAL 은 기본값으로 각 스레드가 독립적인 보안 컨텍스트를 가지며 대부분의 서버 환경에 적합하다. 이외에도 MODE_INHERITABLETHREADLOCAL(하위 스레드가 부모 스레드의 보안 컨텍스트를 상속 받음)과 MODE_GLOBAL(모든 스레드가 공유하는 전역 보안 컨텍스트) 전략이 있으며 필요에 따라 선택할 수 있다.
🍅 SecurityContext
SecurityContextHolder 를 통해 SecurityContext 를 조회할 수 있으며, 이 SecurityContext 는 현재 인증된 사용자의 Authentication 객체를 포함하고 있다.
👒 AuthenticationManager
AuthenticationManager 는 필터가 전달한 인증 정보를 바탕으로 인증을 수행하고, 인증 결과를 반환한다. `AuthenticationManager` 의 호출 후, 반환된 [`Authentication`](#-authentication) 객체가 [`SecurityContextHolder`](#-securitycontextholder) 에 설정된다.
주로 사용하는 구현체는 ProviderManager 다.
🐝 ProviderManager
ProviderManager 는 AuthenticationManager 의 구현체다. ProviderManager 는 AuthenticationProvider 목록 중에서 인증 처리 요건에 맞는 적절한 AuthenticationProvider 를 찾아 인증 처리를 위임한다. AuthenticationProvider 인스턴스 중 아무도 인증할 수 없는 경우, ProviderManger 는 ProviderNotFoundException 을 발생시킨다.

또한 ProviderManager 는 선택적으로 부모 AuthenticationManager 를 구성할 수 있는데, 이 부모는 AuthenticationProvider 중 아무도 인증을 수행할 수 없는 경우에 참조된다. 부모는 모든 유형의 AuthenticationManager 가 될 수 있지만, 일반적으로 ProviderManager 의 인스턴스다.

🍄 AuthenticationProvider
AuthenticationProvider 는 특정 유행의 인증을 수행하는데 사용된다. 예를 들어 DaoAuthenticationProvider 는 사용자 이름과 비밀번호 기반 인증을 지원하며, JwtAuthenticationProvider 는 JWT 토큰 인증을 지원한다. ProviderManager 에는 여러 개의 AuthenticationProvider 인스턴스를 주입할 수 있다.
🌊 인증 흐름(Authentication Flow)

- 사용자가 자격 증명을 제출하면,
AbstractAuthenticationProcessingFilter는HttpServletRequest에서Authentication객체를 생성하여 인증한다. 생성되는Authentication의 유형은AbstractAuthenticationProcessingFilter의 하위 클래스에 따라 달라진다. 예를 들어,UsernamePasswordAuthenticationFilter는HttpServletRequest에서 제출된 사용자 이름과 비밀번호를 바탕으로UsernamePasswordAuthenticationToken을 생성한다. - 그런 다음, 생성된
Authentication객체를 인증을 위해AuthenticationManager에 전달한다. - 인증이 실패하면 다음과 같은 처리가 이루어진다.
SecurityContextHolder가 비워진다.RememberMeServices.loginFail이 호출된다. “Remember Me”가 구성되지 않은 경우, 이 호출은 무시된다.AuthenticationFailureHandler가 호출된다.
- 인증이 성공하면 다음과 같은 처리가 이루어진다.
SessionAuthenticationStrategy가 새로운 로그인을 알린다.Authentication이SecurityContextHolder에 설정된다. 나중에SecurityContext를 저장해 향후 요청에서 자동으로 설정할 수 있도록 하려면SecurityContextRepository#saveContext를 명시적으로 호출해야 한다.RememberMeServices.loginSuccess가 호출된다. “Remember Me”가 구성되지 않은 경우, 이 호출은 무시된다.ApplicationEventPublisher가InteractiveAuthenticationSuccessEvent를 게시한다.AuthenticationSuccessHandler가 호출된다.
참고
Leave a comment