[Spring Security] SecurityContextHolder 이해하기

Session 관련 문제가 발생해 프로젝트의 Security 설정 부분을 살펴보다 Spring Security 부분을 공부를 해야겠다는 생각이 들었다. 그래서 거대한 Spring Security 부분을 차근차근 정리하면서 공부해보려고 한다.

 

SecurityContextHolder


SecurityContextHolder는 인증된 사용자의 정보를 저장하고 있는 저장소이다.

 

SecurityContextHolder의 구조를 사진으로 보면 SecurityContext를 감싸고 있고 SecurityContext 안에 Authentication(인증)이 저장되어 있다.

 

SecurityContextHolder 설정

1. SecurityContext securityContext = SecurityContextHolder.createEmptyContext();
        
2. Authentication authentication = new TestingAuthenticationToken("username", "password", "ROLE_USER");
        
3. securityContext.setAuthentication(authentication);
        
4. SecurityContextHolder.setContext(securityContext);

 

  1. SecurityContextHolder를 이용해 빈(Empty) SecurityContext를 생성해 준다. 여기서 멀티 스레드 환경에서 충돌을 피하기 위해 SecurityContextHolder.getContext().setAuthentication(authentication)으로 SecurityContext를 사용하는 것보다는 새로 생성해줘야 한다.
  2. 인증 객체를 생성한다. 여기서는 간단하게 테스트하기 위해 TestingAuthenticationToken 객체를 생성했다.
  3. SecurityContext에 방금 생성한 Authentication 객체를 저장한다.
  4. SecurityContextHolder 객체에 Authentication 객체를 저장하고 있는 SecurityContext를 저장한다.

 

인증된 정보를 가져와 사용하기 위해서는 아래 코드 예제 처럼 하면 된다.

SecurityContext context = SecurityContextHolder.getContext();
Authentication authentication = context.getAuthentication();
String username = authentication.getName();
Object principal = authentication.getPrincipal();

 

SecurityContextHolder 객체에 저장되어 있는 SecurityContext 객체를 가져와서 해당 SecurityContext객체 안에 저장되어 있는 Authentication 객체를 가져와 인증된 사용자의 정보를 가져올 수 있다.

 

 

ThreadLocal


여기서 나는 SecurityContextHolder 객체가 바로 Authentication 객체를 저장하면 되는 거 아니야? 왜 굳이 Authentication 객체를 SecurityContext가 한번 더 감싸서 SecurityContextHolder객체에 저장하는 거야?라는 생각을 했는데 그 이유는 아래와 같다.

 

우선 SecurityContextHolder는 기본 strategy값인 ThreadLocal을 사용해 각 스레드마다 하나의 인스턴스가 생성되고, 독립적인 SecurityContext를 저장하고 관리한다. 그래서 SecurityContextHolder는 멀티 스레드 환경에서 각 스레드가 서로의 보안 정보에 영향을 미치지 않도록 각각의 스레드에 대해 독립적으로 작동한다.

 

ThreadLocal이란?

ThreadLocal은 한 스레드 내에서만 접근 가능한 데이터 저장 방식을 의미한다. 이렇게 하면 각각의 스레드에서 별도의 SecurityContext를 가질 수 있다. 각 스레드는 자신만의 인증 정보와 권한을 유지할 수 있으며, 다른 스레드의 영향을 받지 않는다.

 

즉, SecurityContextHolder가 SecurityContext를 한 번 더 감싸서 사용하는 이유는 각 스레드마다 고유한 SecurityContext를 유지하여 각 스레드에 저장된 인증 정보가 다른 스레드에 영향을 주지 않고 관리하기 위함이다.

 

 

SecurityContextHolder Strategy


위에서 잠깐 언급했던 SecurityContextHolder Strategy에 대해 알아보자.

 

 

SecurityContextHolder 전략에는 MODE_THREADLOCAL, MODE_INHERITABLETHREADLOCAL, MODE_GLOBAL 총 3가지 전략이 존재한다.

 

  1. MODE_THREADLOCAL (기본값): 이 전략은 위에서 간단하게 알아본 Thread Local 저장소를 사용하여 각 스레드마다 별도의 SecurityContext를 관리한다. 스레드 간에 보안 정보가 분리되어 저장되기 때문에 각 스레드는 자신의 SecurityContext에만 접근할 수 있어 멀티 스레드 환경에서 독립적으로 보안을 유지할 수 있다.
  2. MODE_INHERITABLETHREADLOCAL: 이 전략은 스레드 간에 상속이 가능한 ThreadLocal 저장소를 사용한다. 이로써 부모 스레드의 SecurityContext가 자식 스레드로 상속되어 사용될 수 있다.
  3. MODE_GLOBAL: 모든 스레드가 SecurityContext를 공유하는 전략이다

 

SecurityContextHolder 전략 설정 방법은 두 가지 존재한다.

  1. SecurityContextHolder.setStrategyName() 메서드를 이용해 설정 변경
  2. application.properties 또는 application.yml 파일에서 spring.security.strategy 속성 설정을 통해 변경

 

Reference

https://docs.spring.io/spring-security/site/docs/5.3.2.RELEASE/reference/html5/#servlet-authentication

 

Spring Security Reference

In Spring Security 3.0, the codebase was sub-divided into separate jars which more clearly separate different functionality areas and third-party dependencies. If you use Maven to build your project, these are the modules you should add to your pom.xml. Ev

docs.spring.io