SecurityContextHolder
SecurityContext
를 제공하는 static method(getContext)를 지원한다.
누가 인증되었는가에 대한 자세한 정보(SecurityContext내의 Authentication )를 저장한다.
SecurityContextHolder
가 어떻게 있는지는 신경쓰지 않는다. 만약 value를 가지고 있다면, 현재 인증된 유저가 사용한다.유저가 인증되었음을 나타내는 가장 간단한 방법은 SecurityContextHolder
를 직접 세팅하는것.
SecurityContext context = SecurityContextHolder.createEmptyContext(); //1)
Authentication authentication =
new TestingAuthenticationToken("username", "password", "ROLE_USER"); //2)
context.setAuthentication(authentication);
SecurityContextHolder.setContext(context); //3)
1) 빈 SecurityContext
에서 시작한다.
SecurityContextHolder.getContext().setAuthentication(authentication)
을 사용하는 것보다는 새로운 SecurityContext
를 생성하는 것이 좋다.2) 새로운 Authentication object를 생성한다.
TestingAuthenticationToken
을 사용했지만, 더 일반적인 production 시나리오는 UsernamePasswordAuthenticationToken(userDetails, password, authorities)
를 사용하는 것.3) 마지막으로, SecurityContext
를 SecurityContextHolder
에 세팅한다.
SecurityContextPersistenceFilter
SecurityContextHolder
에 접근하면 된다.SecurityContext context = SecurityContextHolder.getContext();
Authentication authentication = context.getAuthentication();
String username = authentication.getName();
Object principal = authentication.getPrincipal();
Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
SecurityContextHolder
는 detail들을 담기 위해 ThreadLocal
을 사용함.ThreadLocalSecurityContextHolderStrategy
SecurityContext
가 method들에 명시적으로, 즉 argument로 passing되지 않더라도 SecurityContext
를 method에서 사용 가능하다는 것.ThreadLocal
로 하니까 그냥 편하게 정보를 가져올 수 있는 것.SecurityContext
1개.ThreadLocal
이 안전한 방법일 것.FilterChainProxy
는 SecurityContext
가 clear 되었음을 보장해주므로.ThreadLocal
을 사용하는 것이 적합하지 않을 수 있다.SecurityContextHolder
설정을 해줘야 한다.SecurityContextHolder.MODE_GLOBAL
전략을 사용할 수 있음.SecurityContextHolder.MODE_INHERITABLETHREADLOCAL
을 사용하면 된다.principal
- 유저를 식별. username과 password를 식별할 때 보통 UserDetail
의 인스턴스가 됨Principal
로 UserDetails
를 반환.authorities
- GrantedAuthority
의 높은 수준의 권한. role, scope등등 있음.credentials
- 보통 password. authenticate 되고 나면 보통 clear.Authentication
이 저장됨.Authentication.getAuthorities()
메소드를 통해 획득 가능.Colleciton<GrantedAuthority>
형태로 제공됨.ROLE_ADMIN
이런거.GrantedAuthority
는 보통 UserDetailsService
에 의해 load된다.initializeStrategy
private static SecurityContextHolderStrategy strategy;
...
private static void initializeStrategy() {
if (MODE_PRE_INITIALIZED.equals(strategyName)) {
Assert.state(strategy != null, "When using " + MODE_PRE_INITIALIZED
+ ", setContextHolderStrategy must be called with the fully constructed strategy");
return;
}
if (!StringUtils.hasText(strategyName)) {
// Set default
strategyName = MODE_THREADLOCAL;
}
if (strategyName.equals(MODE_THREADLOCAL)) {
strategy = new ThreadLocalSecurityContextHolderStrategy();
return;
}
if (strategyName.equals(MODE_INHERITABLETHREADLOCAL)) {
strategy = new InheritableThreadLocalSecurityContextHolderStrategy();
return;
}
if (strategyName.equals(MODE_GLOBAL)) {
strategy = new GlobalSecurityContextHolderStrategy();
return;
}
// Try to load a custom strategy
try {
Class<?> clazz = Class.forName(strategyName);
Constructor<?> customStrategy = clazz.getConstructor();
strategy = (SecurityContextHolderStrategy) customStrategy.newInstance();
}
catch (Exception ex) {
ReflectionUtils.handleReflectionException(ex);
}
}
...
}
SecurityContextHolderStrategy
타입의 strategy를 초기화해주고 있다.
ThreadLocal, INHERITABLETHREADLOCAL 등의 조건에 따라 설정해준다.
default는 ThreadLocal이다.
즉, SecurityContextHolderStrategy
의 Factory 역할을 수행한다.
setContext
public static void setContext(SecurityContext context) {
strategy.setContext(context);
}
위에서 설정해준 Thread 관련 옵션에 따라 SecurityContext를 지정해준다.
아래 코드는 ThreadLocalSecurityContextHolderStrategy()
의 일부이다.
private static final ThreadLocal<SecurityContext> contextHolder = new ThreadLocal<>();
...
@Override
public void setContext(SecurityContext context) {
Assert.notNull(context, "Only non-null SecurityContext instances are permitted");
contextHolder.set(context); //ThreadLocalMap에 추가해서 Thread별 관리 들어감.
}
private static final ThreadLocal<SecurityContext> contextHolder = new ThreadLocal<>();
는 static으로 지정했는데 thread간 참조문제 없는가?
public interface SecurityContext extends Serializable {
Authentication getAuthentication();
void setAuthentication(Authentication authentication);
}
Authentication
은 실제 유저의 정보를 담는다.SecurityContext
는 Authentication
구현체를 설정된 Thread strategy에 따라 관리.SecurityContextHolder
는 SecurityContext
를 담고 있으며, Thread 관련 strategy를 결정하는 Factory 역할을 한다.