DaoAuthenticationProvider 클래스는 Spring Security에서 사용자 인증을 처리하는 데 사용되는 주요 클래스 중 하나입니다. 이 클래스는 AuthenticationProvider 인터페이스를 구현하여 사용자 정보를 로드하고 인증을 수행합니다.
1. additionalAuthenticationChecks 메서드
이 메서드는 인증 과정에서 추가적인 검사를 수행합니다. 여기서는 사용자가 입력한 비밀번호가 데이터베이스에 저장된 비밀번호와 일치하는지 확인합니다.
- 입력된 비밀번호 확인: authentication.getCredentials()를 사용하여 입력된 비밀번호를 가져옵니다.
- 비밀번호 비교: passwordEncoder.matches()를 사용하여 입력된 비밀번호와 데이터베이스에 저장된 비밀번호를 비교합니다.
- 검사 실패 처리: 비밀번호가 일치하지 않으면 BadCredentialsException을 발생시킵니다.
@Override
protected void additionalAuthenticationChecks(UserDetails userDetails,
UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
if (authentication.getCredentials() == null) {
this.logger.debug("Failed to authenticate since no credentials provided");
throw new BadCredentialsException(this.messages
.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"));
}
String presentedPassword = authentication.getCredentials().toString();
if (!this.passwordEncoder.matches(presentedPassword, userDetails.getPassword())) {
this.logger.debug("Failed to authenticate since password does not match stored value");
throw new BadCredentialsException(this.messages
.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"));
}
}
2. retrieveUser 메서드
이 메서드는 사용자 이름을 기반으로 사용자 정보를 로드합니다.
- 사용자 로드: userDetailsService.loadUserByUsername(username)를 호출하여 사용자 정보를 로드합니다.
- 오류 처리: 사용자 정보가 없거나 다른 문제가 발생하면 예외를 발생시킵니다.
@Override
protected final UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken authentication)
throws AuthenticationException {
prepareTimingAttackProtection();
try {
UserDetails loadedUser = this.getUserDetailsService().loadUserByUsername(username);
if (loadedUser == null) {
throw new InternalAuthenticationServiceException(
"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) {
throw new InternalAuthenticationServiceException(ex.getMessage(), ex);
}
}
3. createSuccessAuthentication 메서드
이 메서드는 인증이 성공한 후 호출됩니다.
- 비밀번호가 타협되었는지 확인: compromisedPasswordChecker를 사용하여 비밀번호가 타협되었는지 확인합니다.
- 비밀번호 인코딩 업그레이드: 필요시 비밀번호 인코딩을 업그레이드합니다.
- 성공적인 인증 객체 생성: super.createSuccessAuthentication()을 호출하여 성공적인 인증 객체를 생성합니다.
@Override
protected Authentication createSuccessAuthentication(Object principal, Authentication authentication,
UserDetails user) {
String presentedPassword = authentication.getCredentials().toString();
boolean isPasswordCompromised = this.compromisedPasswordChecker != null
&& this.compromisedPasswordChecker.check(presentedPassword).isCompromised();
if (isPasswordCompromised) {
throw new CompromisedPasswordException("The provided password is compromised, please change your password");
}
boolean upgradeEncoding = this.userDetailsPasswordService != null
&& this.passwordEncoder.upgradeEncoding(user.getPassword());
if (upgradeEncoding) {
String newPassword = this.passwordEncoder.encode(presentedPassword);
user = this.userDetailsPasswordService.updatePassword(user, newPassword);
}
return super.createSuccessAuthentication(principal, authentication, user);
}
클래스 변수 설명
- passwordEncoder: 비밀번호를 암호화하고 비교하는 데 사용됩니다.
- userDetailsService: 사용자 정보를 로드하는 데 사용됩니다.
- userDetailsPasswordService: 비밀번호를 업데이트하는 데 사용됩니다.
- compromisedPasswordChecker: 비밀번호가 타협되었는지 확인하는 데 사용됩니다.
- userNotFoundEncodedPassword: 타이밍 공격을 방지하기 위해 사용됩니다.
요약
- 사용자가 로그인 시도: 사용자가 로그인 폼에 사용자 이름과 비밀번호를 입력합니다.
- authenticate 메서드 호출: DaoAuthenticationProvider가 authenticate 메서드를 호출하여 인증을 시도합니다.
- 사용자 로드: retrieveUser 메서드가 UserDetailsService를 사용하여 사용자 정보를 로드합니다.
- 비밀번호 비교: additionalAuthenticationChecks 메서드가 입력된 비밀번호와 데이터베이스에 저장된 비밀번호를 비교합니다.
- 인증 성공: 비밀번호가 일치하면 createSuccessAuthentication 메서드가 호출되어 인증 성공 객체를 생성합니다.
- 인증 실패: 비밀번호가 일치하지 않으면 예외가 발생하여 인증이 실패합니다.
이 과정을 통해 Spring Security는 사용자가 입력한 자격 증명이 유효한지 확인하고, 유효한 경우 인증을 완료합니다.
Spring Security에서 비밀번호 검증 과정
- 사용자 입력 비밀번호 해싱:
- 사용자가 로그인 폼에 비밀번호를 입력합니다.
- Spring Security는 이 입력된 비밀번호를 받아 해싱 알고리즘을 통해 해시값으로 변환합니다.
- 예를 들어, 사용자가 입력한 비밀번호가 password123이라고 하면, 해싱 알고리즘을 통해 해시된 결과가 F32C...ADV와 같이 생성됩니다.
- UserDetailsService를 통한 사용자 정보 로드:
- DaoAuthenticationProvider는 UserDetailsService를 사용하여 데이터베이스에서 사용자의 정보를 로드합니다.
- 여기서 로드된 정보에는 사용자의 해시된 비밀번호가 포함됩니다.
- 예를 들어, 데이터베이스에서 로드된 사용자의 해시된 비밀번호가 G22H...BEF라고 가정합니다.
- 비밀번호 검증 (비교):
- DaoAuthenticationProvider의 additionalAuthenticationChecks 메소드가 호출됩니다.
- 이 메소드에서는 사용자가 입력한 비밀번호를 해싱하여 데이터베이스에서 로드된 해시된 비밀번호와 비교합니다.
- 해시 알고리즘을 사용하여 입력된 비밀번호 password123을 해싱한 결과 F32C...ADV가 생성됩니다.
- 이 해시값이 데이터베이스에서 로드된 해시값 G22H...BEF와 일치하는지 비교합니다.