본문 바로가기

Language/Spring Security

SecurityContextRepository / SecurityContextHolderFilter

SecurityContextRepository

  • 스프링 시큐리티에서 사용자가 인증 이후 요청에 대해 계속 사용자의 인증을 유지하기 위해 사용되는 클래스
    • Repository ⇒ 인증 상태를 유지하기 위한 저장소
  • 인증 상태의 영속 메커니즘
    • 사용자가 인증 성공 시 해당 사용자의 인증 정보와 권한(인증 객체)이 SecurityContext에 저장
    • HttpSession을 통해 요청 간 영속이 이루어 지는 방식

인증 요청

 

  1. 사용자 로그인 요청(아이디, 패스워드 입력)
  2. AuthenticationFilter가 해당 정보를 AuthenticationManager에게 전달하여 인증 객체를 AuthenticationFilter에 반환
  3. Authentication 객체를 SecurityContext에 저장
  4. SecurityContext를 명시적으로 저장소(SecurityContextRepository)에 보관
  5. HttpSession에 SecurityContext에 저장

인증 후 요청

  1. /user에 접근 요청
  2. SecurityContextHolderFilter가 컨텍스트 저장소로 부터 컨텍스트를 로드
  3. SecurityContextRepository를 사용하여 세션으로부터 컨텍스트 존재를 확인
  4. HttpSession에 저장된 컨텍스트를 로드
    → Authencation 객체가 null이라면 인증 X, null이 아니라면 인증 O

구조

메서드명 설명
containsContext 현재 사용자를 위한 보안 컨텍스트가 저장소에 있는지 확인
saveContext 인증 요청 완료 시 보안 컨텍스트를 저장
loadDeferredContext 로딩을 지연시켜 필요 시점에 SecurityContext 로드
  • HttpSessionSecurityContextRepository
    • 요청 간에 HttpSession에 보안 컨텍스트를 저장
    • 후속 요청 시 컨텍스트 영속성 유지
  • RequestAttributeSecurityContextRepository
    • ServletRequest에 보안 컨텍스트 저장
    • 후속 요청 시 컨텍스트 영속성 유지 불가
  • NullSecurityContextRepository
    • 세션을 사용하지 않는 인증(JWT, OAuth2)일 경우 컨텍스트 관련 아무런 처리를 하지 않음
  • DelegatingSecurityContextRepository
    • RequestAttributeSecurityContextRepository와 HttpSessionSecurityContextRepository를 동시에 사용할 수 있도록 위임된 클래스
    • 초기화 시 기본으로 설정

SecurityContextHolderFilter

  • SecurityContextRepository를 사용하여 SecurityContext를 얻고 이를 SecurityContextHolder에 설정하는 필터 클래스
  • 필터 클래스는 SecurityContextRepository.saveContext()를 강제로 실행시키지 않고 사용자가 명시적으로 호출되어야 SecurityContext를 저장 가능
    → SecurityContextPersistenceFilter와 다른 점
  • 인증이 지속되어야 하는 지를 각 인증 메커니즘이 독립적으로 선택할 수 있게 하여 더 나은 유연성을 제공, HttpSession에 필요할 때만 저장함으로써 성능 향상
    과거에는 굳이 저장을 하지 않아도 되는 것조차 무조건 저장을 했었기에 다른 사이드 이펙트(?)가 발생했다고 함
    → 이러한 이유로 어떠한 경우는 저장, 어떠한 경우에는 저장하지 않게 선택할 수 있도록 SecurityContextHolderFilter 클래스가 등장한 것 같음

SecurityContext 생성, 저장, 삭제

  • 익명 사용자
    • SecurityContextRepository 사용 → 새로운 SecurityContext 객체를 생성 → SecurityContextHolder에 저장 → 다음 필터로 전달
    • AnonymousAuthenticationFilter(다음 필터)에서 AnonymousAuthenticationToken 객체를 SecurityContext 에 저장
      SecurityContext의 인증 객체는 null이 들어가 있어, 이걸 꺼내서 익명 인증 토근으로 SecurityContext에 다시 저장
  • 인증 요청
    • SecurityContextRepository 사용 → 새로운 SecurityContext 객체를 생성 → SecurityContextHolder에 저장 → 다음 필터로 전달
    • UsernamePasswordAuthenticationFilter(다음 필터) 에서 인증 성공 후 UsernamePasswordAuthentication 객체를 SecurityContext 에 저장
    • SecurityContextRepository 사용 → HttpSession에 SecurityContext 저장→ 커스텀 인증 필터를 만들고 사용할 때 Session에 SecurityContext를 저장하는 코드가 포함되지 않으면 강제적으로 세션에 저장하는 작업을 해주어야 함
      → 폼 인증을 위한 필터(UsernamePasswordAuthentication)에서는 강제적으로 SecurityContext를 세션에 저장하는 역할을 하여 인증을 유지해줌
  • 인증 후 요청
    • SecurityContextRepository 사용 → HttpSession에서 SecurityContext 꺼내어 SecurityContextHolder에서 저장 → 다음 필터로 전달
    • SecurityContext 안에 Authentication 객체가 존재 시인증 유지
  • 클라이언트 응답 시 공통
    • SecurityContextHolder.clearContext() 로 컨텍스트를 삭제
      → 스레드 풀의 스레드일 경우 반드시 필요

SecurityContextHolderFilter 흐름도

  1. 클라이언트 요청 수신
  2. SecurityContextHolderFilter 무조건 작동
  3. SecurityContextRepository에서 HttpSession을 확인
  • SecurityContext에 존재하지 않음
    1. 새로운 SecurityContext 생성하여 SecurityContextHolder에 저장
      • 현재 Authentication 상태는 null
    2. AuthenticationFilter가 인증 수행
    3. SecurityContext에 인증 객체(AuthenticationToken) 저장
    4. SecurityContextRepository를 통해 세션에 SecurityContext 저장
      • Form 인증이라면 위의 과정을 거치지만, 커스텀 인증 필터는 4번의 과정이 없어 명시적으로 저장 수행 필요
  • SecurityContext에 존재
    1. SecurityContext를 로드하여 SecuriyContextHolder에 저장하여 다음 필터로 전달
    2. 다음 필터로 전달하기 전 SecurityContextHolder안에 SecurityContext 삭제
      • 다음 요청에서 SecurityContext안의 인증 객체가 null인 상태에서 시작해야하기 때문

SecurityContextHolderFilter & SecurityContextPersistanceFilter

  • SecurityContextRepository를 사용하여 SecurityContext를 로드하는 것은 동일
  • SecurityContextHolderFilter
    • 응답 시점에 Session에 SecurityContext를 저장하지 않음
  • SecurityContextPersistenceFilter
    • 응답 시점에 Session에 SecutiryContext를 저장

securityContext() API

@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity hhtp) throws Exception{
	http.securityContext(securityContext -> securityContext
				.requiredExplicitSave(true);
	return http.build();
}
  • requirceExplicitSave()
    • SecurityContext를 명시적으로 저장할 것인지 아닌지 여부 설정(Default true)
      • ture ⇒ SecurityContextHolderFilter 실행
      • false ⇒ SecurityContextPersistenceFilter 실행
  • 현재 SecurityContextPersistenceFilter는 Deprecated되어 레거시 시스템 외에는 SecurityContextHolder 사용

CustomAuthenticationFilter & SecurityContextRepository

  • 커스텀한 인증 필터를 구현할 경우 인증 완료 후 SecurityContext를 SecurityContextHolder에 설정한 후 SecurityContextRepository에 저장하기 위한 코드를 명시적으로 작성
securityContextHolderStrategy.setContext(context);
securityContextRepository.saveContext(context, request, response);
  • securityContextRepository는 HttpSessionSecurityContextRespository 혹은 DelegatingSecurityContextRepository 사용