이 시리즈에 나오는 모든 내용은 인프런 인터넷 강의 - 스프링 시큐리티 - Spring Boot 기반으로 개발하는 Spring Security - 에서 기반된 것입니다. 그리고 여기서 인용되는 PPT 이미지 또한 모두 해당 강의에서 가져왔음을 알립니다.
우리가 인증을 수행하고 나면 스프링 시큐리티 내에서는 인증 결과(=인증토큰)를
SecurityContext 라는 곳에 저장하여, 전역적으로 사용이 가능하도록 한다.
그렇다면 대체 SecurityContext
는 무엇이며, 어떻게 전역적으로 사용이 가능한걸까?
이를 알기 위해서는 SecurityContext
및 SecurityContextHolder
의 구조가 어떤지 알고,
이게 스프링 시큐리티 내부의 어느 필터에서 사용되는지를 알아야 한다.
필터는 다음에 알아보고 먼저 SecurityContext
, SecurityContextHolder
클래스에 대해 알아보자.
SecurityContextHolder
전략(Strategy
)에 따라 SecurityContext
의 저장 방식이 다름ThreadLocal
에 저장Authentication
을 꺼내서 사용가능참고:
ThreadLocal
?
쓰레드마다 갖는 고유한 저장공간이라고 생각하면 된다.
SecurityContext
를 감싸는(저장하는) 객체SecurityContext
저장을 위한 ThreadLocal
를 갖고 있는 객체SecurityContext
객체의 저장 방식(전략, Strategy)을 지정MODE_THREADLOCAL
: 스레드당 SecurityContext
객체를 할당, 기본값MODE_INHERITABLETHREADLOCAL
: 메인, 자식 스레드에서 동일한 SecurityContext
사용MODE_GLOBAL
: 프래그램에서 딱 하나의 SecurityContext
만 저장SecurityContextHolder
의 메소드 대부분이 이 전략 클래스의 인스턴스에게 작업을 위임하는 형태로 동작한다.SecurityContextHolder.clearContext()
: 기존 SecurityContext
정보 초기화참고: SecurityContextHolder, SecurityContext 를 통한 인증객체 읽는 법
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
ThreadLocal
이 존재함SecurityContextHolder.clearContext()
SecurityContextHolder > SecurityContext
에 인증 토큰 저장HttpSession
에도 SecurityContext
저장위에서 설명한 것들을 코드로 직접 보고 이해해 보자.
SecurityContextHolder 의 java document
를 읽어보면 앞에 이런 문구가 나온다.
Associates a given SecurityContext with the current execution thread.
This class provides a series of static methods that delegate to an instance of SecurityContextHolderStrategy. (생략)"이 클래스(SecurityContextHolder)는 다양한 static 메소드를 제공하고, 해당 메소드들은 내부적으로
SecurityContextHolderStrategy
에게 위임처리를 한다"
즉 SecurityContextHolder
의 실제 일처리는 SecurityContextHolderStrategy
가
한다는 것을 알 수 있다.
그래서 SecurityContextHolder
초기화 시, initializeStrategy
메소드(위 그림 참고)가 호출되고, 내부적으로 SecurityContextHolderStrategy
를 결정하게 된다.
이때 어떤 모드로 세팅했는지에 따라 달라지는데, 기본값은 MODE_THREADLOCAL
모드이기 때문에
아무 설정을 안하면 ThreadLocalSecurityContextHolderStrategy
를 사용한다.
이름 그대로 쓰레드 로컬을 사용해서 SecurityContext 를 보관하는 전략이다.
MODE_THREADLOCAL
는 스레드당 하나의 SecurityContext
객체를 할당하는 전략이라 언급했다.
그리고 MODE_THREADLOCAL
을 위한 SecurityContextHolder 의 내부 전략(Strategy)가
바로 ThreadLocalSecurityContextHolderStrategy
이다.
SecurityContext 객체를 스레드당 하나만 생성한다는 전략을 위해서 이 전략 클래스는
ThreadLocal<SecurityContext>
필드를 하나 갖고 있다.
그렇다면 이 ThreadLocal 에 저장하는 SecurityContext 에 대해서 알아보자.
밑에 있는 createEmptyContext()
에서 new SecurityContextImpl()
을 통해서
SecurityContext
를 생성하는 것을 확인할 수 있다.
여기서는 Authentication 과 관련된 setter, getter 가 있다는 것만 알고 넘어가자.
참고: SecurityContextHolder, SecurityContext 사용법
// SecurityContext 저장 ( 직접 쓸 일이 많지 않음 ) SecurityContext emptyContext = SecurityContextHolder.createEmptyContext(); emptyContext.setAuthentication(authentication1); SecurityContextHolder.setContext(emptyContext); // SecurityContext 조회 ( 가장 많이 쓰는 방식 ) SecurityContextHolder.getContext(); // SecurityContext 초기화 ( 직접 쓸 일이 많지 않음 ) SecurityContextHolder.clearContext();
@RestController
public class SecurityController {
@GetMapping("/")
public String index(HttpSession session) {
Authentication authentication1 = SecurityContextHolder.getContext().getAuthentication();
Authentication authentication2 = null;
if (session.getAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY)
instanceof SecurityContext securityContext) { // java 17 문법입니다!
authentication2 = securityContext.getAuthentication();
}
System.out.println("authentication1 = " + authentication1);
System.out.println("authentication2 = " + authentication2);
System.out.println("authentication1 hashCode = " + authentication1.hashCode());
System.out.println("authentication2 hashCode = " + authentication2.hashCode());
return "home";
}
}
@Configuration(proxyBeanMethods = false)
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().mvcMatchers("/favicon.ico");
web.ignoring().mvcMatchers("/error");
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin();
}
}
이렇게 하고 콘솔창을 확인해보자.
확인해야 될 점은 크게 2가지다.
SecurityContextHolder
, HttpSession
모두 SecurityContext 를 들고 있다.