본문 바로가기

카테고리 없음

[Spring Boot] Security6 DaoAuthenticationProvider

 

 

 

 

 

 

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: 타이밍 공격을 방지하기 위해 사용됩니다.

요약

  1. 사용자가 로그인 시도: 사용자가 로그인 폼에 사용자 이름과 비밀번호를 입력합니다.
  2. authenticate 메서드 호출: DaoAuthenticationProvider가 authenticate 메서드를 호출하여 인증을 시도합니다.
  3. 사용자 로드: retrieveUser 메서드가 UserDetailsService를 사용하여 사용자 정보를 로드합니다.
  4. 비밀번호 비교: additionalAuthenticationChecks 메서드가 입력된 비밀번호와 데이터베이스에 저장된 비밀번호를 비교합니다.
  5. 인증 성공: 비밀번호가 일치하면 createSuccessAuthentication 메서드가 호출되어 인증 성공 객체를 생성합니다.
  6. 인증 실패: 비밀번호가 일치하지 않으면 예외가 발생하여 인증이 실패합니다.

이 과정을 통해 Spring Security는 사용자가 입력한 자격 증명이 유효한지 확인하고, 유효한 경우 인증을 완료합니다.

 

Spring Security에서 비밀번호 검증 과정

  1. 사용자 입력 비밀번호 해싱:
    • 사용자가 로그인 폼에 비밀번호를 입력합니다.
    • Spring Security는 이 입력된 비밀번호를 받아 해싱 알고리즘을 통해 해시값으로 변환합니다.
    • 예를 들어, 사용자가 입력한 비밀번호가 password123이라고 하면, 해싱 알고리즘을 통해 해시된 결과가 F32C...ADV와 같이 생성됩니다.
  2. UserDetailsService를 통한 사용자 정보 로드:
    • DaoAuthenticationProvider는 UserDetailsService를 사용하여 데이터베이스에서 사용자의 정보를 로드합니다.
    • 여기서 로드된 정보에는 사용자의 해시된 비밀번호가 포함됩니다.
    • 예를 들어, 데이터베이스에서 로드된 사용자의 해시된 비밀번호가 G22H...BEF라고 가정합니다.
  3. 비밀번호 검증 (비교):
    • DaoAuthenticationProvider의 additionalAuthenticationChecks 메소드가 호출됩니다.
    • 이 메소드에서는 사용자가 입력한 비밀번호를 해싱하여 데이터베이스에서 로드된 해시된 비밀번호와 비교합니다.
    • 해시 알고리즘을 사용하여 입력된 비밀번호 password123을 해싱한 결과 F32C...ADV가 생성됩니다.
    • 이 해시값이 데이터베이스에서 로드된 해시값 G22H...BEF와 일치하는지 비교합니다.