Spring/Spring Security

[Spring Security] 3-1. Authentication: Architecture

noahkim_ 2021. 7. 28. 11:14

1. SecurityContextHolder

  • Spring Security의 인증 모델의 핵심

 

  • SecurityContext가 들어있음 (현재 인증된 사용자 정보)
  • 인증이 필요할 떄 꺼내서 사용 권한을 검사하거나 인증 여부를 확인함
  • SecurityContextHolder에 인증 정보가 존재하면, 그 사용자는 인증된 사용자로 간주됨

 

저장 방식

ThreadLocal
  • ThreadLocal을 이용해서 현재 인증 정보인 SecurityContext를 저장함
  • 같은 스레드 내의 메서드들은 SecurityContext를 전달하지 않고도 사용할 수 있음
  • 요청 처리가 끝나면 FilterChainProxy가 SecurityContext를 지워줌

 

저장 전략
전략 설명  
MODE_THREADLOCAL (기본값)
현재 스레드에만 인증 정보 저장
 
MODE_INHERITABLETHREADLOCAL
자식 스레드도 인증 정보 공유
비동기 처리 시, 인증 정보를 물려주기 위해 사용
MODE_GLOBAL
JVM 전체에서 하나의 인증 정보를 공유
전체 인증정보를 모든 스레드에서 사용할 경우
  • 시스템 속성(-Dspring.security.strategy=...) 설정
  • 자바 코드에서 SecurityContextHolder.setStrategyName(...) 호출

 

예시

예) 인증 정보 꺼내기

더보기
SecurityContext context = SecurityContextHolder.getContext();
Authentication authentication = context.getAuthentication();
String username = authentication.getName();
Object principal = authentication.getPrincipal();
Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
  • getName() → 사용자 이름 (username)
  • getPrincipal() → 실제 사용자 정보 (보통 UserDetails)
  • getAuthorities() → 권한(Role) 목록

 

예) 수동 설정

더보기
SecurityContext context = SecurityContextHolder.createEmptyContext();
Authentication authentication =
    new TestingAuthenticationToken("username", "password", "ROLE_USER");
context.setAuthentication(authentication);

SecurityContextHolder.setContext(context);
  1. SecurityContext 생성 및 Authentication 객체 담기
  2. SecurityContextHolder에 SecurityContext 저장

⚠️ SecurityContextHolder.getContext().setAuthentication(...)로 저장할 경우 race condition 위험이 있음

 

2. SecurityContext

  • Authentication 객체를 담는 객체

 

3. Authentication

  • 인증과 권한 부여를 위해 사용하는 가장 핵심적인 인터페이스

 

역할

구분 인증 요청 (인증 전) 인증 완료 (인증 후)
사용 목적 사용자 입력 자격 정보 전달
현재 로그인된 사용자 정보 표현
isAuthenticated() 값 FALSE TRUE
principal 사용자 ID (보통 String)
사용자 정보 객체 (예: UserDetails)
credentials 비밀번호 등 인증 정보
보통 null 처리됨 (보안상 삭제됨)
사용 예 로그인 시도 시 인증 요청
인증된 사용자 접근 권한 확인 시 사용

 

예시) 인증 요청

더보기

 요청

AuthenticationManager.authenticate(...)

 

대표 구현체

new UsernamePasswordAuthenticationToken("id", "pw")

 

예시) 인증 완료

더보기

 요청

SecurityContextHolder.getContext().getAuthentication()

 

대표 구현체

UsernamePasswordAuthenticationToken(UserDetails, null, authorities)

 

필드

필드명 설명
principal
사용자 정보.
보통 UserDetails 구현체 (username, email, id 등)
credentials
비밀번호와 같은 민감 정보.
인증 후 보통 삭제됨 (null 처리)
authorities
권한 목록.
GrantedAuthority 리스트 (ROLE_USER, SCOPE_read 등)

 

4. GrantedAuthority

  • 사용자에게 부여된 권한을 나타내는 인터페이스
  • prefix 사용 (ROLE_)

 

예) 꺼내기

더보기
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
Collection<? extends GrantedAuthority> authorities = auth.getAuthorities();

 

5. AuthenticationManager

  • 인증을 수행하는 핵심 인터페이스
  • 인증 필터가 Authentication 객체를 만들고 이 객체를 AuthenticationManager에 전달함
  • AuthenticationManager는 authenticate()를 호출하여 SecurityContextHolder에 인증 정보를 저장함

 

6. ProviderManager

  • AuthenticationManager의 대표 구현체
  • 여러 개의 AuthenticationProvider에게 인증을 위임하는 구조

 

인증 처리 흐름

  1. 클라이언트 요청이 들어오면 인증 필터가 Authentication 객체 생성
  2. ProviderManager.authenticate(authentication) 호출
  3. 내부적으로 등록된 AuthenticationProvider 목록을 순서대로 탐색
    • AuthenticationProvider는 자신이 처리할 수 있는 Authentication만 처리함 (supports())
    • 인증에 성공하면 인증된 Authentication 반환
    • 인증 실패 시, 다음 provider로 위임
  4. 모든 provider가 인증을 처리하지 못하면, ProviderNotFoundException 예외 발생

 

Parent AuthenticationManager

  • ProviderManager는 부모 AuthenticationManager를 가질 수 있음
  • 모든 provider가 실패했을 때, 부모에게 인증 시도 위임

 

보안 처리

  • credentials 자동 삭제
  • 기본적으로 인증 성공 후 Authentication 객체에서 민감 정보는 삭제됨

 

7. AuthenticationProvider

  • 특정 방식의 인증을 수행하는 컴포넌트
  • supports() 메서드로 자신이 처리 가능한 인증 객체인지 확인 후, 인증을 수행함

 

대표 구현체

Provider 인증 방식
DaoAuthenticationProvider
아이디 + 비밀번호 (DB 조회)
JwtAuthenticationProvider JWT 토큰
LdapAuthenticationProvider LDAP 인증
커스텀 Provider
예: Kakao OAuth, QR 인증 등 직접 구현 가능

 

8. Request Credentials with AuthenticationEntryPoint

AuthenticationEntryPoint

  • 자격 증명 요청을 보낼 때 사용하는 인터페이스
  • 모든 인증이 실패할 때, 어떻게 클라이언트에게 인증을 유도할 지 결정하는 엔트리 포인트
  • 사용자의 인증 흐름을 시작시킴
상황 동작 방식
브라우저 기반
로그인 페이지로 리다이렉트 (/login)
REST API
401 Unauthorized + WWW-Authenticate 헤더 응답
OAuth2
인증 서버로 리다이렉트

 

 

9. AbstractAuthenticationProcessingFilter

  • 사용자의 인증 요청을 처리하는 기반 필터 클래스
  • ex) UsernamePasswordAuthenticationFilter

 

인증 실패 시

  1. SecurityContextHolder.clearContext() → 보안 컨텍스트 초기화
  2. RememberMeServices.loginFail() 호출 (Remember-Me 기능이 설정되어 있다면 실패 처리)
  3. AuthenticationFailureHandler 실행 → 실패 후 처리 (예: 에러 페이지 리다이렉트)

 

인증 성공 시

  1. SessionAuthenticationStrategy 호출 → 세션 고정 보호, 세션 생성 등 처리
  2. 인증된 Authentication을 SecurityContextHolder에 저장
    • 필요하면 SecurityContextRepository.saveContext(...)로 명시적 저장 (서블릿 필터 체인 이후에도 유지되게 하려면)
  3. RememberMeServices.loginSuccess() 호출 (remember-me 쿠키 설정 등)
  4. InteractiveAuthenticationSuccessEvent 이벤트 발행 (이벤트 리스너에서 후처리 가능)
  5. AuthenticationSuccessHandler 실행 → 로그인 성공 후 처리 (예: 대시보드 리다이렉트)

 

 

참고