스프링 시큐리티 주요 아키텍처 컴포넌트
SecurityContextHolder
SecurityContext
SecurityContextHolder로 접근할 수 있으며 인증한 사용자의 Authentication을 가지고 있다.Authentication
SecurityContext에서 현재 사용자를 제공하기 위한 AuthenticationManager에 대한 입력이 될 수 있다.GrantedAuthority
Authentication에서 접근 주체(principal)에 부여한 권한이다.AuthenticationManager
ProviderManager
AuthenticationManager의 일반적인 구현체AuthenticationProvider
ProverManger에 의해 사용되어 특정 타입의 인증을 수행AuthenticationEntryPoint
AbstractAuthenticationProcessingFilter
FilterSecurityContext를 포함하고 있다.

SecurityContextHolder에 어떻게 값이 들어갔는지는 상관하지 않고 값이 있다면 인증된 사용자가 있다고 본다.
사용자가 인증되었음을 나타내는 가장 쉬운 방법은 직접 값을 넣는 것이다.
SecurityContext context = SecurityContextHolder.createEmptyContext();
Authentication authentication =
new TestingAuthenticationToken("username", "password", "ROLE_USER");
context.setAuthentication(authentication);
SecurityContextHolder.setContext(context);
SecurityContextHolder.getContext.setAuthentication()을 사용해선 안 된다.Authentication 객체를 생성한다.
UserPasswordAuthenticationToken을 주로 사용한다.SecurityContexHolder에 SecurityContext를 설정한다.인증된 사용자 정보를 얻으려면 SecurityContextHolder를 통해 접근할 수 있다.
SecurityContext context = SecurityContextHolder.getContext();
Authentication authentication = context.getAuthentication();
String username = authentication.getName();
Object principal = authentication.getPrincipal();
Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
SecurityContextHolder는 ThreadLocal에 정보를 저장하기에 동일 스레드라면 항상 SecurityContext에 접근할 수 있다.
FilterChainProxy가 항상 ThreadLocal을 비워준다.SecurityContext는 Authentication 객체를 가지고 있다.Authentication은 다음 2가지 목적으로 제공된다.
AuthenticationManager의 입력으로 사용되어 인증된 사용자의 credentials을 제공한다.SecurityContext에서 현재 인증된 Authentication을 얻을 수 있다.Authentication은 다음을 포함한다.
principal: 사용자를 나타내며 username/password로 인증했을 시 UserDetails 인스턴스로 나타난다.credentials: 주로 패스워드이다. 대부분 유츌되지 않도록 인증한 다음 비운다.authorities: GratedAuthority의 추상화로 사용자에게 부여한 권한이다.Authentication.getAuthorities()로 얻을 수 있다.
GrantedAuthority의 Collection이 반환된다.UserDetailsService가 GrantedAuthority를 로드한다.Authentication을 SecurityContextHolder에 설정하는 건 AuthenticationManager를 호출한 객체(시큐리티의 필터)가 담당한다.SecurityContextHolder에 AuthenticationManager를 거치지 않고 Authentication을 직접 설정할 수도 있다.AuthenticationManager의 구현체는 보통 ProviderManager이다.AuthenticaitonManager의 구현체ProviderManager는 동작을 AuthenticaitonProvider의 List에 위임한다.
AuthenticationProvider는 인증을 성공시키거나 실패시킬 수 있다.AuthenticationProvider에 결정을 맡긴다.AuthenticationProvider가 인증을 판단하지 못하면 특별한 예외인 ProviderNotFoundException이 발생한다.
ProviderFoundException이 발생했다는 뜻은 넘겨진Authentication유형을 지원하는ProviderManager가 설정되지 않았다는 것이다.

ProviderManager는 인증에 성공하면 리턴하는 Authentication 객체에 있는 민감 credential 정보를 지운다.
HttpSession에 길게 유지하지 않는다.애플리케이션에서 사용자 객체를 캐싱하는 경우
Authentication이 credential을 지워버린다면 캐시된 값으로는 더 이상 인증할 수 없다. 따라서 캐시를 사용한다면 이를 고려하여 구현해야 한다. 객체의 복사본을 만들거나ProviderManager의eraseCredentialsAfterAuthentication프로퍼티를 비활성화시켜도 된다.
ProviderManager에 여러 AuthenticationProvider를 주입할 수 있다.
AuthenticationProvider는 특정 유형의 인증을 수행한다.AuthenticationManager 빈만으로 여러 유형의 인증을 모두 처리할 수 있다.
ProviderManager가 여러 인증 유형을 지원하는 AuthenticationProvider들을 가지고 있기에AuthenticationEntryPoint는 클라이언트에게 credentials을 요청하는 HTTP 응답을 보낼 때 사용한다.
AbstractAuthentiationProcessingFilter는 사용자 credentials을 인증하는 base Filter다.
AuthenticationEntryPoint가 credential 요청 응답을 보내는 것
1. 사용자가 credential을 제출하면 AbstractAuthenticationProcessingFitler가 HttpServletRequest에서 Authentication을 생성한다.
AbstractAuthenticationProcessingFitler의 하위 클래스에 따라 다르다.UsernamePasswordAuthenticationFilter라면 UsernamePasswordAuthenticationToken 타입이 생성된다.
Authentication은 인증되기 위해 AuthenticationManager에 전달된다.SecurityContextHolder가 비워진다.RememberMeServices.loginFial을 실행하는데 remember me를 설정하지 않았다면 동작하지 않는다.AuthenticationFailureHandler가 동작한다.
SessionAuthenticationStrategy가 새로운 로그인을 통지 받는다.SecurityContextHolder에 Authentication이 설정되고 SecurityContextPersistenceFilter가 SecurityContext를 HttpSession에 저장한다.RememberMeServices.loginSuccess가 동작하는데 remember me가 설저오디지 않았다면 동작하지 않는다.ApplicationEventPublisher가 InteractiveAuthenticationSuccessEvent를 발행한다.AuthenticationSuccessHandler가 동작한다.