아이디 비번이 일치한지 확인할 때 어떻게 할 것 같나요?

아이디 비번을 Req Dto로 받아, 아이디로 유저를 찾아, 비밀번호를 확인한다.

는 시큐리티를 사용하지 않을 때, 얘기입니다.

시큐리티를 사용한 비밀번호 검증은 다음과 같이 이뤄집니다.

// 1. Login ID/PW 를 기반으로 AuthenticationToken 생성
UsernamePasswordAuthenticationToken authenticationToken = reqDto.toAuthentication();

// 2. 실제로 검증 (사용자 비밀번호 체크) 이 이루어지는 부분
Authentication authentication = authenticationManagerBuilder.getObject()
    .authenticate(authenticationToken);

thenticationManagerBuilder.getObject().authenticateUserDetailsService 에서 작동을 하니, 커스텀을 해보겠습니다.

/**
 * AuthenticationManagerBuilder 의  authenticate 커스텀
 */
@Service
@RequiredArgsConstructor
public class CustomUserDetailsService implements UserDetailsService {

  private final MemberRepository memberRepository;

  /**
   * UserDetails 와 Authentication 의 비밀번호 비교 + 검증
   * 비밀번호는 AuthenticationProvider 에서 실제로 비교함
   *
   * @param username 회원의 아이디
   * @return 회원 Entity -> UserDetail 객체로 변환
   * @throws UsernameNotFoundException
   */
  @Override
  @Transactional
  public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
    Member member = memberRepository.findByAccount(username).orElseThrow(
        () -> new BusinessException(username, "account", ErrorCode.MEMBER_ACCOUNT_NOT_FOUND)
    );

    UserDetails userDetails = createUserDetails(member);
    return userDetails;
  }

  /**
   * UserDetails 객체로 만들어서 리턴
   */
  private UserDetails createUserDetails(Member member) {
    GrantedAuthority grantedAuthority = new SimpleGrantedAuthority(
        member.getAuthority().toString());

    return new User(
        String.valueOf(member.getId()),
        member.getPassword(),
        Collections.singleton(grantedAuthority)
    );
  }
}

loadUserByUsername 를 통해서, 아이디가 존재하는지 확인을 하였지만, 비밀번호가 일치하는지 확인하는 로직은 전혀 없습니다.

비밀번호 검증 자체는 실제로 일어나고 있지만, 제가 원하는 예외로 처리가 되지 않았기에, 문제가 되었습니다.

AuthenticationProvider 가 실제로 비밀번호를 검증한다는 사실을 알게되었습니다.

Untitled

따라서 커스텀을 하여, 원하는 예외 메시지를 보낼 수 있었습니다.

/**
 * 비밀번호를 확인할 때, 원하는 예외 처리를 위해서 커스텀
 */
@Service
@RequiredArgsConstructor
public class CustomAuthenticationProvider implements AuthenticationProvider {

  private final CustomUserDetailsService userDetailsService;
  private final PasswordEncoder passwordEncoder;

  /**
   * 아이디 비번 등 검증
   */
  @Override
  public Authentication authenticate(Authentication authentication) throws AuthenticationException {
    // 회원 아이디 얻기
    String userId = authentication.getName();
    // 회원 비밀번호 얻기
    String userPassword = (String) authentication.getCredentials();

    // 아이디로 회원 찾기
    UserDetails member = userDetailsService.loadUserByUsername(userId);

    // 비밀번호 확인
    if (!passwordEncoder.matches(userPassword, member.getPassword())) {
      throw new BusinessException(null, "password", ErrorCode.MEMBER_PASSWORD_BAD_REQUEST);
    }

    // 다시 만들어서 반환
    Authentication newAuth = new UsernamePasswordAuthenticationToken(
        userId, userPassword, member.getAuthorities()
    );

    return newAuth;
  }

  /**
   * 타입 체크
   */
  @Override
  public boolean supports(Class<?> authentication) {
    return authentication.equals(UsernamePasswordAuthenticationToken.class);
  }
}