[Spring] Spring Security 기본 이해 - (4)
목적 : Authentication 이해
- 핵심 용어 :SpringContextPersistenceFilter, Authentication Flow, AuthenticatioManager, AuthenticationProvider
Authentication Flow
AuthenticaionManager는 각 provider마다 supports()를 통해 적절한 Authentication provider를 찾는다.
찾았다면 UserDetailsService를 통해 시스템 속에 존재하는 유저가 있는지를 찾는다. UserDetails 인스턴스를 리턴하는데, 어떠한 Authenticaion provider가 verify하고 authenticate 했는지에 대한 정보가 담겨 있다.
성공했다면 Pricipal과 Authorities가 Authenticaion 객체에 있고, 그렇지 않다면 AuthenticationException 예외를 발생한다.
AuthenticationManager : Spring Security Filter를 어떻게 사용할지를 정의하는 API. ProviderManager에 의해 상속되는데 authentication의 특정 타입을 수행(username/password, 등... )할지 알고 있다.
Controller에 의해 SecurityContextHolder에 Authentication을 리턴하는데 이 때 AuthenticationManage를 생성한다. Filter 인스턴스를 통합하지 않는다면 SecurityContextHolder를 직접 설정하고 AuthenticationManager를 사용하지 않아도 된다.
AuthenticationManager은 보통 ProvideManager에 상속으로 사용된다. ProviderManager는 AuthenticationManager 인스턴스 리스트를 구성하고 있다. 각 AuthenticatonProvider가 authentication에 성공/실패에 대한 것을 보여준다.
여러개의 ProviderManager 인스턴스는 동일한 AuthenticationManager를 공유한다. 여러 AuthenticationProviders가 특정한 Authentication 기능을 하고, 하나의 AuthenticationManager 빈을 지지한다.
(SpringFilterChain과 유사)
예시 코드는 다음과 같다.
public class AtlassianCrowdAuthenticationProvider implements AuthenticationProvider {
Authentication authenticate(Authentication authentication)
throws AuthenticationException {
String username = authentication.getPrincipal().toString();
String password = authentication.getCredentials().toString();
atlassian.crowd.User user = callAtlassianCrowdRestService(username, password); // (1)
if (user == null) {
throw new AuthenticationException("could not login");
}
return new UserNamePasswordAuthenticationToken(user.getUsername(), user.getPassword(), mapToAuthorities(user.getGroups())); // (2)
}
// other method ignored
}
Authentication가 성공했다면 usernamePasswordAuthenticaionToken을, 그렇지 않다면 AuthenticaionException을 발생시킨다.
AuthenticationProvider : Authenticaion 객체를 생성하여 Authentication 성공 후 사용자의 credentials를 저장. AuthenticaionProvider 객체 여러개를 ProviderManager 객체에게 주입할 수 있다.
기본적으로, 만약 Authentication이 성공적이라면, ProviderManager는 Authentication 객체에 있는 민감 credentials 정보를 비우려고 한다. HttpSession에 더이상 필요하지 않은 정보가(비밀번호등) 있는 것을 막는다.
이는 사용자 객체 캐시를 사용할 때 문제가 생긴다 (http 성질 stateless 상태를 기억하지 않음.). 이를 해결하기 위해 AuthenticationProvider에 Authenticaion 객체를 넣어서 필요한 정보를 저장한다.
(대안적으로는 eraseCredentialAfterAuthentication 프로퍼티를 사용하여 이를 막을 수 있음.)
[ 참고 ]
https://www.linkedin.com/pulse/how-does-spring-security-works-internally-ayush-jain/