클라이언트로 요청을 받으면 요청된 인증을 UsernamePasswordAuthenticationFiler(위 그림에선 AuthenticationFilter)에서 처리한다.
요청된 인증을 처리한 후 HttpServletRequest객체로 UsernamePasswordAuthenticationToken에 전달한다.
2.1 UsernamePasswordAuthentication에서 username과 password를 추출해서 Authentication 객체를 생성한다.(setAuthenticated = false).
생성된 Authentication 객체를 AuthManager 인터페이스를 구현하고 있는 ProviderManager의 Authentication authentication(Authentication authentication)로 넘긴다.
이 메소드는 for문을 돌면서 AuthenticationProvider의 provider, 즉 각 provider가 인증을 할 수 있는지 여부를 확인합니다. 모든 provider 중에서 해당 인증을 처리할 수 있는 provider를 찾아 (supports메소드가 true) 실제 인증 절차인 authenticate() 메서드를 실행시킵니다
ProviderManager.java
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
Class<? extends Authentication> toTest = authentication.getClass();
AuthenticationException lastException = null;
AuthenticationException parentException = null;
Authentication result = null;
Authentication parentResult = null;
int currentPosition = 0;
int size = this.providers.size();
for (AuthenticationProvider provider : getProviders()) {
if (!provider.supports(toTest)) { //인증 가능한지 체크
continue;
}
if (logger.isTraceEnabled()) {
logger.trace(LogMessage.format("Authenticating request with %s (%d/%d)",
provider.getClass().getSimpleName(), ++currentPosition, size));
}
try {
result = provider.authenticate(authentication); //인증이 가능하면 여기서 인증
if (result != null) {
copyDetails(authentication, result);
break;
}
}
catch (AccountStatusException | InternalAuthenticationServiceException ex) {
prepareException(ex, authentication);
// SEC-546: Avoid polling additional providers if auth failure is due to
// invalid account status
throw ex;
}
catch (AuthenticationException ex) {
lastException = ex;
}
참고로 AuthenticationProvider 인터페이스를 구현한 클래스 이름은 AbstractUserDetailsAuthenticationProvider이다.
그후 다시 AbstractUserDetailsAuthenticationProvider.authenticate()의 additionalAuthenticationChecks()를 통해 해당 유저의 비밀번호 일치 여부를 체크한다. 이 메서드 또한 DaoAuthenticationProvider클래스에 구현되어있다. additionalAuthenticationChecks()는 비밀번호가 일치하는지 여부를 확인하는 메서드이다.
DaoAuthenticationProvider
@Override
protected final UserDetails as(String username, UsernamePasswordAuthenticationToken authentication)
throws AuthenticationException {
prepareTimingAttackProtection();
try{
UserDetails loadedUser = this.getUserDetailsService().loadUserByUsername(username); //UserDetails 호출
//생략
}
}
이렇게 비밀번호까지 확인했다면 AbstractUserDetailsAuthenticationProvider 클래스의 authenticate()는 createSuccessAuthentication()메서드를 리턴합니다.
protected Authentication createSuccessAuthentication(Object principal, Authentication authentication, UserDetails user){
UsernamePasswordAuthenticationToken result = new
UsernamePasswordAuthenticationToken(principal,
authentication.getCredentials(),
this.authoritiesMapper.mapAuthorities(user.getAuthorities()));
result.setDetails(authentication.getDetails());
this.logger.debug("Authenticated user");
return result;
}
이때 authenticated 값은 true 가 되고 해당 객체는 인증 완료된 Authentication 객체가 만들어진다.
10. 검증된 토큰을 SecurityContextHolder안에 있는 SecurityContext에 저장한다. (아래 Authentication객체의 인증 후 참고)
인증된 사용자의 상세 정보를 보관해주는 장소(Authentication을 담고있는 곳)
인증된 사용자의 정보(Authentication)를 가짐
String Security에 사용자의 정보를 담는 인터페이스
Spring Security에서 사용자의 정보를 가져오는 인터페이스
메소드 리턴타입 설명 loadUserByUsername UserDetails 유저의 정보를 불러와서 UserDetails로 리턴
authentication 객체의 인증을 처리 후 다시 authentication 객체를 돌려주는 인터페이스
이를 통해 인증되면 isAuthenticated(boolean)의 값을 TRUE로 바꿔준다.
AuthenticationManager 인터페이스의 구현체
인증을 AuthenticationProvider들에게 위임하고 그 중 하나의 AuthenticationProvider 구현 클래스가 인증에 성공하면
아이디와 비밀번호가 유효한지 검사