아이디 비번을 Req Dto로 받아, 아이디로 유저를 찾아, 비밀번호를 확인한다.
는 시큐리티를 사용하지 않을 때, 얘기입니다.
시큐리티를 사용한 비밀번호 검증은 다음과 같이 이뤄집니다.
// 1. Login ID/PW 를 기반으로 AuthenticationToken 생성
UsernamePasswordAuthenticationToken authenticationToken = reqDto.toAuthentication();
// 2. 실제로 검증 (사용자 비밀번호 체크) 이 이루어지는 부분
Authentication authentication = authenticationManagerBuilder.getObject()
.authenticate(authenticationToken);
thenticationManagerBuilder.getObject().authenticate 은 UserDetailsService 에서 작동을 하니, 커스텀을 해보겠습니다.
/**
* 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 가 실제로 비밀번호를 검증한다는 사실을 알게되었습니다.

따라서 커스텀을 하여, 원하는 예외 메시지를 보낼 수 있었습니다.
/**
* 비밀번호를 확인할 때, 원하는 예외 처리를 위해서 커스텀
*/
@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);
}
}