MODE_INHERITABLETHREADLOCAL : 메인 스레드와 자식 스레드에 관하여 동일한 SecurityContext 를 유지된다.
MODE_GLOBAL : 응용 프로그램에서 단 하나의 SecurityContext를 저장한다.
그 전략은 아래와 같이 구현되어있다.
public class SecurityContextHolder {
public static final String MODE_THREADLOCAL = "MODE_THREADLOCAL";
public static final String MODE_INHERITABLETHREADLOCAL = "MODE_INHERITABLETHREADLOCAL";
public static final String MODE_GLOBAL = "MODE_GLOBAL";
public static final String SYSTEM_PROPERTY = "spring.security.strategy";
private static String strategyName = System.getProperty("spring.security.strategy");
private static SecurityContextHolderStrategy strategy;
private static int initializeCount = 0;
// ... 이하 생략
}
아래와 같이 설정할 수 있다.
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
SecurityContextHolder.setStrategyName(SecurityContextHolder.MODE_INHERITABLETHREADLOCAL);
}
}
이렇게 하면 자식 스레드에서도 공유가능하다.
자식스레드? -> 예를들어 로그인하고 다른 페이지로 이동하면 -> 로그인할때의 스레드가 부모, 다른 페이지 요청할 때 쓰레드가 자식이다.
SecurityContextHolder 가 ThreadLocal 을 가지고 있고, ThreadLocal 은 SecurityContext 를 가지고 있다.
최종적으로 세션에 저장되는 것을 확인할 수 있다.
아래와 같이 세션에 저장된 Authentication 객체와 SecurityContextHolder 에 담겨있는 Authentication 를 비교해볼 수 있다.
사용자의 로그인을 시도하게되면 UsernamePasswrodAutheticationFilter 가 전달받고 Authentication 를 만든다.
principal속성에는 사용자의 아이디를 저장하고 Credentials속성에는 패서워드를 저장한다 저장된 정보들은 인증검증을 받기위해서 사용된다.
AuthenticationMannager가 인증객체를 가지고 인증처리를 시작한다 인증에 성공되면 Authentication를 새로 만든다.
AuthenticationMannager로 받기전의 Authentication 와 받고나서 새로생성된 Authentication 는 동일한타입으로 만든다.
차이점은 principal과 Credentials를 보면되는데 처음에는 사용자의 아이디와 패스워드를 저장하지만 이후에는 인증에 성공한 결과를 담고 Authorities 권한 목록을 담는다.
최종적으로 SecurityContextHolder로 Authentication 를 저장한다.
코드는 아래와 같다.
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
// 사용자 정보
Object principal = authentication.getPrincipal();
// 사용자 권한
Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
boolean authenticated = authentication.isAuthenticated();
Authentication : Principal과 GrantAuthority 제공한다.
Principal
“누구"에 해당하는 정보이다.
객체는 UserDetails 타입이다.
GrantAuthority:
“ROLE_USER”, “ROLE_ADMIN”등 Principal이 가지고 있는 “권한”을 나타낸다.
인증 이후, 인가 및 권한 확인할 때 이 정보를 참조한다.
UserDetails : 애플리케이션이 가지고 있는 유저 정보와 스프링 시큐리티가 사용하는 Authentication 객체 사이의 어댑터이다.
UserDetailsService : 유저 정보를 UserDetails 타입으로 가져오는 DAO (Data Access Object) 인터페이스다.
ThreadLocal
Java.lang 패키지에서 제공하는 쓰레드 범위 변수이다. -> 즉, 쓰레드 수준의 데이터 저장소이다.
ThreadLocal은 Thread마다 고유한 영역을 가지고 있는 곳에 저장된 변수로 각각의 Thread안에서 유효한 변수다.
ThreadLocal 은 스프링 시큐리티 외에도 많이 사용되니 잘 알아두는 것이 좋다.
public class AccountContext {
private static final ThreadLocal<Account> ACCOUNT_THREAD_LOCAL
= new ThreadLocal<>();
public static void setAccount(Account account) {
ACCOUNT_THREAD_LOCAL.set(account);
}
public static Account getAccount() {
return ACCOUNT_THREAD_LOCAL.get();
}
}
위의 코드는 보면 ThreadLocal 의 set, get 메서드를 구현한 것이다.
위의 메소드를 통해서 요청에 따라 다른 ThreadLocal 을 공유하는 것을 확인할 수 있다.
WebMVC 기반으로 프로젝트를 만든다는 가정하에 대부분의 경우, 요청 1개의 Thread 1개가 생성된다.
이때 ThreadLocal을 사용하면 Thread마다 고유한 공간을 만들수가 있고 그곳에 SecurityContext를 저장할 수 있다.
그러나 ThreadLocal만 강제로 사용해야하는 것은 아니며 원하면 SecurityContext 공유 전략을 바꿀 수 있다.
Spring Security의 기본적인 Security Context 관리 전략은 ThreadLocal을 사용하는 ThreadLocalSecurityContextHolderStrategy 다.
아래와 같은 전략들을 가진다.
MODE_THREADLOCAL
ThreadLocalSecurityContextHolderStrategy를 사용한다. -> 디폴트 설정 모드이다.
ThreadLocal을 사용하여 같은 Thread안에서 SecurityContext를 공유합니다.
MODE_INHERITABLETHREADLOCAL
InheritableThreadLocalSecurityContextHolderStrategy를 사용한다. - InheritableThreadLocal을 사용하여 자식 Thread까지도 SecurityContext를 공유한다.