Spring/Spring Security

[Spring Security] 3-1. Authentication: Architecture

noahkim_ 2021. 7. 28. 11:14

1. SecurityContextHolder

  • 현재 실행 흐름에서 사용중인 SecurityContext를 보관하고 조회하기 위한 정적 접근 지점
  • ✅ 인증이 필요할 때 꺼내서 사용 권한을 검사하거나 인증 여부를 확인함

 

예) 인증 정보 꺼내기

더보기
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 저장

더보기
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 위험이 있음

 

SecurityContextHolderStrategy

전략 설명 특징
MODE_THREADLOCAL (기본값)
현재 스레드에만 인증 정보 저장
요청 처리가 끝나면 SecurityContextPersistenceFilter가 SecurityContext를 지움
MODE_INHERITABLETHREADLOCAL
자식 스레드도 인증 정보 공유
직접 생성한 자식 스레드에는 사용할 수 있지만 스레드 풀 기반 비동기 처리엔 주의 필요
MODE_GLOBAL
JVM 전체에서 하나의 인증 정보를 공유
일반적인 다중 사용자 웹 애플리케이션에선 부적합

 

셋팅 방법

더보기
  • JVM 시스템 속성: -Dspring.security.strategy=...
  • Java 코드: SecurityContextHolder.setStrategyName

 

2. SecurityContext

  • Authentication 객체를 담는 컨테이너

 

3. Authentication

  • 인증 요청과 인증 결과를 표현하는 핵심 인터페이스
  • ✅ 인가 과정에서는 현재 사용자와 권한 정보를 제공함


필드

필드명 설명 예시 / 특징
principal 사용자 정보
- Username/Password: UserDetails 구현체
- JWT: Jwt
- OAuth2 Login: OAuth2User or OidcUser
- Anonymous: 익명 사용자 정보
credentials 민감 정보 (비밀번호 등)
인증 후 삭제됨 (null 처리)
authorities 권한 목록
GrantedAuthority 리스트 (ROLE_USER, SCOPE_read 등)

 

역할

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

 

예시) 인증 요청

더보기

 요청

AuthenticationManager.authenticate(...)

 

대표 구현체

new UsernamePasswordAuthenticationToken("id", "pw")

 

예시) 인증 완료

더보기

 요청

SecurityContextHolder.getContext().getAuthentication()

 

대표 구현체

UsernamePasswordAuthenticationToken(UserDetails, null, authorities)

 

4. GrantedAuthority

  • 사용자에게 부여된 권한을 나타내는 인터페이스
  • ✅ 인가는 Authentication의 권한 목록을 조회하고 getAuthority()가 반환하는 문자열을 비교하여 이루어짐

 

예) 꺼내기

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

 

prefix 규칙

  • ROLE_: 역할을 나타내는 관례로 쓰임. hasRole() API를 제공함 (prefix를 자동으로 붙여 권한을 검사함)
  • SCOPE_: JWT 또는 OAuth2 Resource Server에서 토큰의 scope 권한을 GrantedAuthority로 변환할 때 쓰임

 

5. AuthenticationManager

  • 인증을 수행하는 핵심 인터페이스
  • ✅ 인증의 진입점 역할을 담당함 (인증 필터가 Authentication 객체를 만들고 이 객체를 AuthenticationManager에 전달함)
  •  인증에 성공하면 완성된 Authentication을 반환함

 

6. ProviderManager

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

 

인증 처리 흐름

  1. 클라이언트 요청이 들어오면 인증 필터가 Authentication 객체를 생성함
  2. ProviderManager.authenticate(authentication) 호출
    • null 반환 시, 다음 provider로 위임
    • 일반 AuthenticationException 예외 시, 해당 예외를 기억하고 계속 진행함 (다음 provider에 위임)
    • ex) BadCredentialsException, UsernameNotFoundException 등
  3. 내부적으로 등록된 AuthenticationProvider 목록을 순서대로 탐색
    • 인증에 성공하면 인증된 Authentication 반환
    • 지원하지 않을 경우, 다음 provider로 위임됨 (supports()가 false)
    • 판단 포기 시, null이 반환되며 다음 provider로 위임됨 (이번 요청에 대해서는 최종 인증 결정을 내리지 않겠다는 뜻)
  4. 전달된 Authentication 타입을 지원하는 provider를 찾지 못하면, ProviderNotFoundException 예외 발생

 

Parent AuthenticationManager

  • ProviderManager는 부모 AuthenticationManager를 가질 수 있음
  • 현재 ProviderManager의 Provider 목록에서 최종 인증 결과를 얻지 못했을 때, 부모에게 인증 시도를 위임할 수 있음

 

보안 처리

  • 인증 성공 후 Authentication 객체에서 민감 정보(credentials)는 보통 null 처리됨

 

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 응답
HTTP Basic / Bearer Token
401 Unauthorized + WWW-Authenticate 헤더 응답
OAuth2
OAuth2 인증 시작 URL로 리다이렉트

 

 

9. AbstractAuthenticationProcessingFilter

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

 

인증 성공 시

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

 

인증 실패 시

  • 계정 상태: LockedException, DisabledException, AccountExpiredException, CredentialsExpiredException 등
  • 내부 시스템 오류: InternalAuthenticationServiceException 등 (db 장애, 외부 인증 시스템 오류 등)
  1. SecurityContextHolder.clearContext() → 보안 컨텍스트 초기화
  2. RememberMeServices.loginFail() 호출  Remember-Me 기능이 설정되어 있다면 실패 처리
  3. AuthenticationFailureHandler 실행 → 실패 후 처리 (예: 에러 페이지 리다이렉트)

 

참고