SecurityContext 

Authentication 객체가 저장되는 보관소로 필요 시 언제든지 Authentication 객체를 꺼내어 쓸 수 있도록 제공되는 클래스
ThreadLocal 에 저장되어 한 쓰레드 내에서 참조가 가능하다.
인증이 완료되면 HttpSession 에 저장되어 어플리케이션 전반에 걸쳐 전역적인 참조가 가능하다

 

 


 

 

SecurityContextHolder

SecurityContext을 감싸고 있는 클래스로 SecurityContext를 저장하는 방식이 4가지가 있다.

  1. MODE_THREADLOCAL : 스레드당 SecurityContext 객체를 할당, 기본값
  2. MODE_INHERITABLETHREADLOCAL : 메인 스레드와 자식 스레드에 관하여 동일한 SecurityContext 를 유지
  3. MODE_GLOBAL :  응용 프로그램에서 단 하나의 SecurityContext를 저장한다
  4. MODE_PRE_INITIALIZED : (찾아봐야함)

 

 


 

 

그림으로 보는 플로우

 

 

유저가 Login을 시도하면 ThreadLocal에 할당이 되고 인증객체를 만들어 인증필터가 인증을 시도한다.

 

 

 

만약 인증에 실패하면 SecurityContextHolder.clearContext() 로 SecurityContext 기존 정보 초기화한다.

 

 

 

성공하게 된다면 ThreadLocal이 SecurityContext를 담은 형태로 SecurityContextHolder에 저장하게 되고 최종적으로 HttpSession 에 저장되어 어플리케이션 전반에 걸쳐 전역적인 참조가 가능하다

 

 


 

 

코드로 보는 플로우

 

로그인을 시도한다.

 

 

 

 

로그인 후  인증이 성공하면 SecurityContextHolder에 인증객체가 저장되고 자원에 접근하게 된다. 인증객체를 얻는 방법은 2가지가 있는데 첫번째 방법은 SecurityContextHolder.getContext().getAuthentication(); 이용해 인증객체를 얻는 것이고
두번째 방법은 HttpSession 인자로 받고 세션에 "SPRING_SECURITY_CONTEXT" 키값으로 저장된 인증객체를 가져오는 방법이다.

 

 

 

만약 SecurityContext 가  MODE_THREADLOCAL  모드라면 자식쓰레드와 메인쓰레드간에 SecurityContext를 공유하지 않기 때문에 아래 코드에서의 authentication는 NULL 이 나오게 된다.

@GetMapping("/thread")
public String thread() {
    new Thread(

        new Runnable() {
            @Override
            public void run() {
                Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
            }
        }

    ).start();

    return "thread";
}

 

 

 

 

아래 처럼 MODE_INHERITABLETHREADLOCAL 모드로 설정해주면 자식쓰레드와 메인 쓰레드간의 SecurityContext를 공유하게 된다.

	@Bean
	public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
		http
			.authorizeRequests()
			.anyRequest()
			.permitAll();
		http
			.formLogin();

		SecurityContextHolder.setStrategyName(SecurityContextHolder.MODE_INHERITABLETHREADLOCAL);
		return http.build();
	}

 

 

복사했습니다!