본문 바로가기

Language/Spring Security

인증 컨텍스트 - SecurityContext / SecurityContextHolder

시큐리티 인증 / 인가 흐름도

 

  • SecurityContext
Authentication 저장 현재 인증된 사용자의 Authentication 객체를 저장
ThreadLocal 저장소 사용 SecurityContextHolder 를 통해 접근되며 ThreadLocal 저장소를 사용해 각 스레드가 자신만의 보안 컨텍스트를 유지

클라이언트마다 스레드 생성 → 스레드마다 로컬 저장소 존재 → 로컬 저장소에 시큐리티 컨텍스트 저장
⇒ 각 스레드마다 독립적인 컨텍스트 객체를 가짐
*스레드마다 독립적인 컨텍스트를 가지기 때문에 다른 스레드의 컨텍스트에 접근할 수 없어 자신만의 보안 컨텍스트가 유지 된다는 것
애플리케이션 전반에 걸친 접근성 애플리케이션의 어느 곳에서나 접근 가능하며 현재 사용자의 인증 상태나 권한을 확인하는 데 사용

 

  • SecurityContextHolder
SecurityContext 저장 현재 인증된 사용자의 Authentication 객체를 담고 있는 SecurityContext 객체를 저장
전략 패턴 사용 다양한 저장 전략을 지원하기 위해 SecurityContextHolderStrategy 인터페이스를 사용
기본 전략 MODE_THREADLOCAL
전략 모드 직접 지정 SecurityContextHolder.setStrategyName(String)

 

  • SecurityContextHolder 저장 모드
MODE_THREADLOCAL 기본 모드로, 각 스레드가 독립적인 보안 컨텍스트를 가짐
→ 대부분의 서버 환경에 적합
MODE_INHERITABLETHREADLOCAL 부모 스레드로부터 자식 스레드로 보안 컨텍스트가 상속되며 작업을 스레드 간 분산 실행하는 경우 유용할 수 있음
MODE_GLOBAL 전역적으로 단일 보안 컨텍스트를 사용하며 서버 환경에서는 부적합하며 주로 간단한 애플리케이션에 적합

구조

 

  • clearContext() : 현재 컨텍스트 삭제
  • getContext() : 현재 컨텍스트 반환
  • getDeferredContext() : 현재 컨텍스트를 반환하는 Supplier 반환
  • setContext(SecurityContext context) : 현재 컨텍스트를 저장
  • setDeferredContext(Supplier<SecurityContext> deferredContext) : 현재 컨텍스트를 반환하는 Supplier 를 저장
  • createEmptyContext() : 새롭고 비어 있는 컨텍스트를 생성

SecurityContext 참조 및 삭제

  • 참조 - SecurityContexHolder.getContextHolderStrategy().getContext()
  • 삭제 - SecurityContexHolder.getContextHolderStrategy().clearContext()

 

SecurityContextHolder & SecurityContext 구조

  • 스레드마다 할당 되는 전용 저장소에 SecurityContext 를 저장하기 때문에 동시성의 문제가 없음
  • 스레드 풀에서 운용되는 스레드일 경우 새로운 요청이더라도 기존의 ThreadLocal이 재사용될 수 있기 때문에 클라이언트로 응답 직전에 항상 SecurityContext를 삭제
    • 클라이언트1의 할당이 끝나고 새로운 클라이언트4가 할당이 되었을 때, ThreadLocal1의 SecurityContext가 남아있으면, 클라이언트1의 인증 정보를 가지고 있게 됨

SecurityContextHolderStrategy 사용하기

  • 기존 방식
    • SecurityContextHolder를 통해 SecurityContext에 정적으로 접근할 때 여러 애플리케이션 컨텍스트가 SecurityContextHolderStrategy를 지정하려고 하는 경우 경쟁 조건을 만들 수 있음
SecurityContext context = SecurityContextHolder.createEmptyContext(); 
context.setAuthentication(authentication); 
SecurityContextHolder.setContext(context);

 

  • 변경 방식
    • 애플리케이션이 SecurityContext를 정적으로 접근하는 대신 SecurityContextHolderStrategy 를 자동 주입이 될 수 있도록 함
    • 각 애플리케이션 컨텍스트는 자신에게 가장 적합한 보안 전략을 사용 가능
SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder.getContextHolderStrategy();
SecurityContext context = securityContextHolderStrategy.createEmptyContext(); 
context.setAuthentication(authentication); 
securityContextHolderStrategy.setContext(context);

정리

  • SecurityContextHolder에는 여러 개의 TheradLocal이 있고, 각각의 ThreadLocal에는 SecurityContext를 가지고 있고, 각각의 SecurityContext에는 각각의 Authentication(인증 정보)가 저장되어 있음
    → 대표적으로 그려지는 그림은 SecurityContextHolder > SpringContext > Authentication
  • SecurityContextHolder에서 SecurityContext로 바로 접근하지 말고, SecurityContextHolder.getContextHolderStragtegy를 통해 접근하는 것을 습관화