AuthenticationManager로 부터 인증을 위임받아 실제로 인증을 진행하는 객체, AuthenticationProvider의 구현체로는 AbstractUserDetailsAuthenticationProvider 와 DaoAuthenticationProvider 가 존재한다.
AuthenticationProvider 의 인증 과정
전달받는 authentication 에서 username을 가져온 뒤 DB에서 해당 하는 유저 정보를 가져온다.
인증을 진행하기 전 DB로부터 가져온 UserDetails 객체에 대한 유효성 검사를 진행한다.
전달 받은 Authentication 객체내의 Credential 정보와 UserDetails 객체의 Password와 값이 일치하는지 확인한다.
username에 대한하는 UserDetails 객체를 못 찾을 경우 UsernameNotFoundException 예외를 던지고
Credentials가 Password와 다를 경우 BadCredentialsException 예외를 던진다.
인증 후 다시한번 UserDetails 객체의 유효성 검사를 진행하고, 캐시에 UserDetails 객체를 저장한다.
UserDetails를 이용해 검증하는 AbstractUserDetailsAuthenticationProvider
AbstractUserDetailsAuthenticationProvider 클래스는 AuthenticationProvider를 구현한 클래스다. AbstractUserDetailsAuthenticationProvider 클래스는 전달 받은 Authentication 객체와 기존에 저장된 UserDetails 객체를 이용해 인증을 진행하는 로직이 구현 돼 있다.
AbstractUserDetailsAuthenticationProvider.java
@Override public Authentication authenticate(Authentication authentication)throws AuthenticationException { Assert.isInstanceOf(UsernamePasswordAuthenticationToken.class, authentication, () -> this.messages.getMessage("AbstractUserDetailsAuthenticationProvider.onlySupports", "Only UsernamePasswordAuthenticationToken is supported"));
DB에 저장된 User 정보를 가져오기 위한 DaoAuthenticationProvider
DaoAuthenticationProvider 클래스는 AbstractUserDetailsAuthenticationProvider 를 상속해 구현한 클래스다. DaoAuthenticationProvider 클래스에는 주로 DB로부터 User 정보를 조회 및 가져온 User 정보를 검증하는 로직이 구현 돼 있다.
DB에 저장된 User 정보 가져오기
DaoAuthenticationProvider.java
@Override protectedfinal UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException { prepareTimingAttackProtection(); // username을 이용해 사용자 정보를 가져온다. try { UserDetailsloadedUser=this.getUserDetailsService().loadUserByUsername(username); if (loadedUser == null) { thrownewInternalAuthenticationServiceException( "UserDetailsService returned null, which is an interface contract violation"); } return loadedUser; } catch (UsernameNotFoundException ex) { mitigateAgainstTimingAttack(authentication); throw ex; } catch (InternalAuthenticationServiceException ex) { throw ex; } catch (Exception ex) { thrownewInternalAuthenticationServiceException(ex.getMessage(), ex); } }
Authentication 객체의 Credentials 정보와 UserDetails 객체의 비밀번호가 일치하는지 검증
DaoAuthenticationProvider.java
protectedvoidadditionalAuthenticationChecks(UserDetails userDetails, UsernamePasswordAuthenticationToken authentication)throws AuthenticationException { if (authentication.getCredentials() == null) { this.logger.debug("Failed to authenticate since no credentials provided"); thrownewBadCredentialsException(this.messages .getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials")); } StringpresentedPassword= authentication.getCredentials().toString(); if (!this.passwordEncoder.matches(presentedPassword, userDetails.getPassword())) { this.logger.debug("Failed to authenticate since password does not match stored value"); thrownewBadCredentialsException(this.messages .getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials")); } }
UserDetailsChecker 객체를 이용한 UserDetails 객체에 대한 검증
UserDetailsChecker 객체는 DB로부터 혹은 캐시에 저장된 UserDetails 객체에 대한 유효성을 검증한다.
@Override // 계정이 잠겼는지, 사용가능한지, 만료됐는지 확인한다. publicvoidcheck(UserDetails user) { if (!user.isAccountNonLocked()) { AbstractUserDetailsAuthenticationProvider.this.logger .debug("Failed to authenticate since user account is locked"); thrownewLockedException(AbstractUserDetailsAuthenticationProvider.this.messages .getMessage("AbstractUserDetailsAuthenticationProvider.locked", "User account is locked")); } if (!user.isEnabled()) { AbstractUserDetailsAuthenticationProvider.this.logger .debug("Failed to authenticate since user account is disabled"); thrownewDisabledException(AbstractUserDetailsAuthenticationProvider.this.messages .getMessage("AbstractUserDetailsAuthenticationProvider.disabled", "User is disabled")); } if (!user.isAccountNonExpired()) { AbstractUserDetailsAuthenticationProvider.this.logger .debug("Failed to authenticate since user account has expired"); thrownewAccountExpiredException(AbstractUserDetailsAuthenticationProvider.this.messages .getMessage("AbstractUserDetailsAuthenticationProvider.expired", "User account has expired")); } } }
인증 전 Authentication 객체
인증 전 Authenticatin 객체내의 principal에는 username이 credentials는 password가 들어있고, 인증 유무를 나타내는 authenticated 값이 false로 나타나 있다.
인증 후 Authentication 객체
인증을 진행한 후 인증 전과 다르게 Authenticatin 객체의 principal에는 UserDeails 를 구현한 객체가 들어갔고 authenticated 값이 true가 됐다. 최종적으로 반환 될 때는 Authenticatin 객체에서 credentials를 지우고 반환한다.