인증 아키텍처
Spring Security 인증 처리의 핵심 구성 요소와 객체 간 관계
목차
1. Authentication
개념
인증은 특정 자원에 접근하려는 사용자의 신원을 확인하는 방법이다. 일반적으로 사용자 이름과 비밀번호를 입력하여 인증이 수행되면 신원을 알고 권한을 부여할 수 있다.
Authentication은 사용자의 인증 정보를 저장하는 토큰 개념의 객체로, 인증 이후 SecurityContext에 저장되어 전역적으로 참조가 가능하다.
Authentication 인터페이스
Authentication은 Principal 인터페이스를 상속한다.
| 메서드 | 반환 타입 | 설명 |
|---|---|---|
getPrincipal() | Object | 인증 주체. 인증 요청 시 사용자 이름, 인증 후에는 UserDetails 객체 |
getCredentials() | Object | 자격 증명. 대개 비밀번호. 인증 후 보안상 제거됨 |
getAuthorities() | Collection<GrantedAuthority> | 인증 주체에게 부여된 권한 목록 |
getDetails() | Object | 인증 요청에 대한 추가 세부 사항 (IP 주소, 인증서 일련 번호 등) |
isAuthenticated() | boolean | 인증 상태 반환 |
setAuthenticated(boolean) | void | 인증 상태 설정 |
인증 전후 토큰 변화
→ AuthenticationProvider"| AFTER subgraph AFTER["인증 후"] direction TB B1["Authentication"]:::token_after B2["principal = UserDetails"]:::field B3["credentials = null"]:::field_dim B4["authorities = [ROLE_USER]"]:::field B5["authenticated = true"]:::field_green B1 --- B2 --- B3 --- B4 --- B5 end AFTER -->|"저장"| SC subgraph SC["SecurityContextHolder"] direction TB SC1["SecurityContext"]:::ctx SC2["Authentication"]:::ctx SC1 --- SC2 end classDef token_before fill:#5c3d00,stroke:#d29922,color:#ffffff,font-weight:bold classDef token_after fill:#1a3a2a,stroke:#3fb950,color:#ffffff,font-weight:bold classDef field fill:#2d333b,stroke:#768390,color:#e6edf3 classDef field_red fill:#5c1a1a,stroke:#f85149,color:#ffffff classDef field_green fill:#1a3a2a,stroke:#3fb950,color:#ffffff classDef field_dim fill:#2d333b,stroke:#768390,color:#8b949e classDef ctx fill:#1c3a5f,stroke:#58a6ff,color:#ffffff
인증 흐름 코드 레벨
인증 필터가 Authentication 객체를 생성하고, AuthenticationManager에 전달하여 인증을 수행한다. 인증 성공 시 완전히 채워진 Authentication 객체를 SecurityContext에 저장한다.
// AuthenticationFilter에서의 인증 흐름 (요약)
// 1. 인증 전 Authentication 생성
UsernamePasswordAuthenticationToken token =
new UsernamePasswordAuthenticationToken(username, password);
// → principal = "user", credentials = "1111", authenticated = false
// 2. AuthenticationManager에 위임
Authentication authResult = authenticationManager.authenticate(token);
// 3. 인증 성공 후 SecurityContext에 저장
SecurityContext context = SecurityContextHolder.createEmptyContext();
context.setAuthentication(authResult);
SecurityContextHolder.setContext(context);
// → principal = UserDetails, credentials = null,
// authorities = [ROLE_USER], authenticated = true
2. SecurityContext / SecurityContextHolder
SecurityContext
| 특성 | 설명 |
|---|---|
| Authentication 저장 | 현재 인증된 사용자의 Authentication 객체를 저장한다 |
| ThreadLocal 저장소 사용 | SecurityContextHolder를 통해 접근되며 각 스레드가 자신만의 보안 컨텍스트를 유지한다 |
| 전역 접근성 | 애플리케이션 어디에서나 접근 가능하며 현재 사용자의 인증 상태나 권한 확인에 사용된다 |
SecurityContextHolder
SecurityContextHolder는 SecurityContext를 저장하고 관리하는 클래스다. SecurityContextHolderStrategy 인터페이스를 통해 다양한 저장 전략을 지원한다.
| 저장 모드 | 설명 |
|---|---|
MODE_THREADLOCAL | 기본 모드. 각 스레드가 독립적인 보안 컨텍스트를 가진다. 대부분의 서버 환경에 적합 |
MODE_INHERITABLETHREADLOCAL | 부모 스레드로부터 자식 스레드로 보안 컨텍스트가 상속된다 |
MODE_GLOBAL | 전역적으로 단일 보안 컨텍스트를 사용한다. 서버 환경에서는 부적합 |
ThreadLocal 기반 구조
Authentication"]:::sc TL2 --> SC2["SecurityContext
Authentication"]:::sc TL3 --> SC3["SecurityContext
Authentication"]:::sc classDef client fill:#1c3a5f,stroke:#58a6ff,color:#ffffff classDef thread fill:#2d333b,stroke:#768390,color:#e6edf3 classDef holder fill:#3d1f5c,stroke:#bc8cff,color:#ffffff classDef tl fill:#5c3d00,stroke:#d29922,color:#ffffff classDef sc fill:#1a3a2a,stroke:#3fb950,color:#ffffff
SecurityContextHolderStrategy API
// SecurityContextHolderStrategy 주요 메서드
void clearContext(); // 현재 컨텍스트 삭제
SecurityContext getContext(); // 현재 컨텍스트 조회
Supplier<SecurityContext> getDeferredContext(); // 지연 로딩 Supplier 조회
void setContext(SecurityContext context); // 컨텍스트 저장
void setDeferredContext(Supplier<SecurityContext> d); // 지연 로딩 Supplier 저장
SecurityContext createEmptyContext(); // 빈 컨텍스트 생성
SecurityContext 사용 방법
// 기존 방식 — SecurityContextHolder에 직접 접근
SecurityContext context = SecurityContextHolder.createEmptyContext();
context.setAuthentication(authentication);
SecurityContextHolder.setContext(context);
// 권장 방식 — SecurityContextHolderStrategy를 주입받아 사용
SecurityContextHolderStrategy strategy = SecurityContextHolder.getContextHolderStrategy();
SecurityContext context = strategy.createEmptyContext();
context.setAuthentication(authentication);
strategy.setContext(context);
// → 각 애플리케이션 컨텍스트가 자신에게 가장 적합한 전략을 사용할 수 있다
SecurityContext 참조: SecurityContextHolder.getContextHolderStrategy().getContext()
SecurityContext 삭제: SecurityContextHolder.getContextHolderStrategy().clearContext()
3. AuthenticationManager
개념
AuthenticationManager는 인증 필터로부터 Authentication 객체를 전달 받아 인증을 시도하는 인터페이스다. 인증에 성공하면 사용자 정보, 권한 등을 포함한 완전한 Authentication 객체를 반환한다.
주로 사용하는 구현체는 ProviderManager이며, AuthenticationManagerBuilder에 의해 생성된다.
ProviderManager 동작 구조
ProviderManager는 여러 AuthenticationProvider를 관리하며, 목록을 순차적으로 순회하면서 인증 요청을 처리한다. 적절한 AuthenticationProvider를 찾아 인증 처리를 위임한다.
(AuthenticationManager 구현체)"]:::manager PM --> P_PARENT["parent: ProviderManager"]:::manager_parent PM --> AP1["DaoAuthenticationProvider"]:::provider PM --> AP2["BasicAuthenticationProvider"]:::provider PM --> AP3["RememberMeAuthenticationProvider"]:::provider P_PARENT --> AP4["DaoAuthenticationProvider
(기본 제공)"]:::provider AP1 -->|"Authentication"| RESULT["인증 완료"]:::success classDef filter fill:#1c3a5f,stroke:#58a6ff,color:#ffffff classDef manager fill:#3d1f5c,stroke:#bc8cff,color:#ffffff,font-weight:bold classDef manager_parent fill:#2d333b,stroke:#768390,color:#e6edf3 classDef provider fill:#5c3d00,stroke:#d29922,color:#ffffff classDef success fill:#1a3a2a,stroke:#3fb950,color:#ffffff
ProviderNotFoundException과 함께 인증이 실패한다.
AuthenticationManager 사용 방법 — HttpSecurity에서 얻기
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
// AuthenticationManagerBuilder를 통해 AuthenticationManager 획득
AuthenticationManagerBuilder builder =
http.getSharedObject(AuthenticationManagerBuilder.class);
AuthenticationManager authenticationManager = builder.build();
// build()는 최초 한번만 호출해야 한다. 이후에는 getObject()로 참조
http
.authorizeHttpRequests(auth -> auth
.requestMatchers("/api/login").permitAll()
.anyRequest().authenticated())
.authenticationManager(authenticationManager) // HttpSecurity에 저장
.addFilterBefore(customFilter(http, authenticationManager),
UsernamePasswordAuthenticationFilter.class);
return http.build();
}
// AuthenticationManager는 빈이 아니므로 파라미터로 전달받아 사용
public CustomAuthenticationFilter customFilter(
HttpSecurity http, AuthenticationManager authenticationManager) {
CustomAuthenticationFilter filter = new CustomAuthenticationFilter();
filter.setAuthenticationManager(authenticationManager);
return filter;
}
AuthenticationManager 사용 방법 — 직접 생성
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http.authorizeHttpRequests(auth -> auth.anyRequest().authenticated());
http.formLogin(Customizer.withDefaults());
http.addFilterBefore(customFilter(), UsernamePasswordAuthenticationFilter.class);
return http.build();
}
@Bean // @Bean으로 선언이 가능하다
public CustomAuthenticationFilter customFilter() {
List<AuthenticationProvider> list1 = List.of(new DaoAuthenticationProvider());
ProviderManager parent = new ProviderManager(list1);
List<AuthenticationProvider> list2 = List.of(
new AnonymousAuthenticationProvider("key"),
new CustomAuthenticationProvider());
ProviderManager authenticationManager = new ProviderManager(list2, parent);
CustomAuthenticationFilter filter = new CustomAuthenticationFilter();
filter.setAuthenticationManager(authenticationManager);
return filter;
}
4. AuthenticationProvider
개념
AuthenticationProvider는 사용자의 자격 증명을 확인하고 인증 과정을 관리하는 인터페이스다. 사용자가 시스템에 액세스하기 위해 제공한 정보(ID/비밀번호)가 유효한지 검증한다.
다양한 유형의 인증 메커니즘을 지원할 수 있다. 표준 사용자 이름/비밀번호 기반, 토큰 기반, 지문 인식 등을 처리할 수 있다.
| 메서드 | 설명 |
|---|---|
authenticate(Authentication) | AuthenticationManager로부터 Authentication을 전달 받아 인증을 수행한다 |
supports(Class<?>) | 해당 인증 유형을 처리할 수 있는지 검사한다 |
AuthenticationProvider 인증 흐름
(username + password)"| AP["AuthenticationProvider"]:::provider AP --> AUTH["authenticate()"]:::process AUTH --> CHK1["사용자 유무 검증"]:::check AUTH --> CHK2["비밀번호 검증"]:::check AUTH --> CHK3["보안 강화 처리"]:::check CHK1 -->|"N"| FAIL["AuthenticationException"]:::fail CHK2 -->|"N"| FAIL CHK3 -->|"Y"| RESULT["Authentication
(UserDetails + Authorities)"]:::success classDef manager fill:#3d1f5c,stroke:#bc8cff,color:#ffffff classDef provider fill:#5c3d00,stroke:#d29922,color:#ffffff,font-weight:bold classDef process fill:#2d333b,stroke:#768390,color:#e6edf3 classDef check fill:#2d333b,stroke:#768390,color:#e6edf3 classDef fail fill:#5c1a1a,stroke:#f85149,color:#ffffff classDef success fill:#1a3a2a,stroke:#3fb950,color:#ffffff
AuthenticationProvider 사용 방법 — 빈 등록
// 1. 빈을 한 개만 정의할 경우
// → DaoAuthenticationProvider를 자동으로 대체한다
@Bean
public AuthenticationProvider customAuthenticationProvider() {
return new CustomAuthenticationProvider();
}
// 2. 빈을 두 개 이상 정의할 경우
// → AuthenticationManagerBuilder를 통해 등록
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
AuthenticationManagerBuilder managerBuilder =
http.getSharedObject(AuthenticationManagerBuilder.class);
managerBuilder.authenticationProvider(customAuthenticationProvider());
managerBuilder.authenticationProvider(customAuthenticationProvider2());
http.authorizeHttpRequests(auth -> auth.anyRequest().authenticated());
http.formLogin(Customizer.withDefaults());
return http.build();
}
5. UserDetailsService
개념
UserDetailsService는 사용자와 관련된 상세 데이터를 로드하는 인터페이스다. 사용자의 신원, 권한, 자격 증명 등의 정보를 포함할 수 있다. 이 인터페이스를 사용하는 클래스는 주로 AuthenticationProvider이며, 사용자가 시스템에 존재하는지 여부와 사용자 데이터를 검색하고 인증 과정을 수행한다.
| 메서드 | 설명 |
|---|---|
loadUserByUsername(String) | 사용자의 이름을 통해 사용자 데이터를 검색하고 UserDetails 객체로 반환한다. 사용자가 없으면 UsernameNotFoundException |
UserDetailsService 흐름
UserDetailsService 사용 방법
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
AuthenticationManagerBuilder managerBuilder =
http.getSharedObject(AuthenticationManagerBuilder.class);
// 아래 두 방식은 동일한 처리를 한다
managerBuilder.userDetailsService(customUserDetailsService());
http.userDetailsService(customUserDetailsService());
http.authorizeHttpRequests(auth -> auth.anyRequest().authenticated());
http.formLogin(Customizer.withDefaults());
return http.build();
}
@Bean
public UserDetailsService customUserDetailsService() {
return new CustomUserDetailsService();
}
6. UserDetails
개념
UserDetails는 사용자의 기본 정보를 저장하는 인터페이스로서, Spring Security에서 사용하는 사용자 타입이다. 저장된 사용자 정보는 추후 인증 절차에서 사용되기 위해 Authentication 객체에 포함되며, 구현체로서 User 클래스가 제공된다.
| 메서드 | 반환 타입 | 설명 |
|---|---|---|
getUsername() | String | 사용자 이름을 반환한다. null을 반환할 수 없다 |
getPassword() | String | 사용자 인증에 사용된 비밀번호를 반환한다 |
getAuthorities() | Collection<GrantedAuthority> | 사용자에게 부여된 권한을 반환한다. null을 반환할 수 없다 |
isAccountNonExpired() | boolean | 계정의 유효 기간이 지났는지 확인. 만료된 계정은 인증할 수 없다 |
isAccountNonLocked() | boolean | 사용자가 잠겨 있는지 확인. 잠긴 사용자는 인증할 수 없다 |
isCredentialsNonExpired() | boolean | 비밀번호의 유효 기간이 지났는지 확인 |
isEnabled() | boolean | 사용자가 활성화되었는지 확인. 비활성화된 사용자는 인증할 수 없다 |
UserDetails와 Authentication의 관계
━━━━━━━━━━━
username
password
authorities
enabled"]:::userdetails UD -->|"값 매핑"| AUTH["Authentication
━━━━━━━━━━━
principal = UserDetails
credentials =
authorities = authorities
authenticated = true"]:::auth classDef provider fill:#3d1f5c,stroke:#bc8cff,color:#ffffff classDef service fill:#5c3d00,stroke:#d29922,color:#ffffff classDef repo fill:#2d333b,stroke:#768390,color:#e6edf3 classDef db fill:#1c3a5f,stroke:#58a6ff,color:#ffffff classDef userdetails fill:#5c3d00,stroke:#d29922,color:#ffffff,font-weight:bold classDef auth fill:#1a3a2a,stroke:#3fb950,color:#ffffff,font-weight:bold
정리
- Authentication — 인증 정보를 담는 토큰 객체. 인증 전에는 username/password, 인증 후에는 UserDetails/Authorities를 포함한다
- SecurityContext / SecurityContextHolder — Authentication을 저장하는 컨테이너. ThreadLocal 기반으로 스레드별 독립적인 보안 컨텍스트를 관리한다
- AuthenticationManager (ProviderManager) — 인증 필터로부터 Authentication을 받아 적절한 AuthenticationProvider에 위임한다. 부모 ProviderManager를 통한 계층 구조를 지원한다
- AuthenticationProvider — 실제 인증 로직을 수행한다. 사용자 유무 검증 → 비밀번호 검증 → 보안 강화 처리 순서로 진행되며, 실패 시 AuthenticationException을 발생시킨다
- UserDetailsService — 사용자 이름으로 DB에서 사용자 데이터를 검색하여 UserDetails 객체로 반환하는 인터페이스
- UserDetails — 사용자의 기본 정보(이름, 비밀번호, 권한, 계정 상태)를 담는 인터페이스. Authentication 객체의 principal로 저장된다
'Java & Spring > Spring Security' 카테고리의 다른 글
| 06. 예외 처리 (0) | 2026.08.05 |
|---|---|
| 05. 세션 관리 (0) | 2026.08.05 |
| 04. 인증 상태 영속성 (0) | 2026.08.04 |
| 02. 인증 프로세스 (0) | 2026.08.01 |
| 01. 초기화 과정 이해 (0) | 2026.08.01 |