본문으로 건너뛰기

Spring Security: There is no PasswordEncoder mapped for the id "null" 오류 해결

2023년 1월 10일에 작성되었고, 블로그를 이전하며 옮겨온 포스트입니다.
여기에서 원본을 확인할 수 있습니다.

문제 상황

비밀번호 재설정 API를 개발하는 도중, 변경할 새로운 비밀번호가 기존 비밀번호와 일치하면 변경할 수 없도록 예외 처리를 진행하는 과정에서 There is no PasswordEncoder mapped for the id "null" 에러를 맞닥뜨렸다. 오류가 지시하고 있는 곳은 SignServiceImpl에서의 passwordEncoder.matches() 해당 부분이었다.

원인

if(passwordEncoder.matches(user.get().getPassword(), resetPasswordRequestDto.getPassword())) {
	...
}

기존 등록된 사용자가 있다면 해당 사용자 객체의 비밀번호와 비밀번호 재설정 DTO에 입력된 비밀번호를 비교하여, 재설정할 비밀번호와 기존 비밀번호가 같다면 변경을 거부하는 코드이다.

제대로 된 원인을 확인하기 전, PasswordEncoder의 matches 함수가 어떻게 구성되어 있는 지에 대해 먼저 확인해야 한다.

boolean matches(CharSequence var1, String var2);

간단히 말하면 var1과 var2가 같다면 true를, 다르다면 false를 반환한다. 하지만, 간과한 점이 하나 있었다.

var1에는 rawPassword, 즉, 암호화 처리가 되기 전의 비밀번호가 입력되어야 하고 var2에는 encodedPassword, 즉, 암호화 된 비밀번호가 입력되어야 한다는 것이다. 간단히 정리하자면 다음과 같다.

boolean matches(CharSequence rawPassword, String encodedPassword);

이제 기존의 코드를 확인해 보자.

passwordEncoder.matches(user.get().getPassword(), resetPasswordRequestDto.getPassword())

user.get().getPassword() 암호화 된 해시 값이 var1에 제시되었고, resetPasswordRequestDto.getPassword() 암호화 되지 않은 일반 문자열의 값이 var2에 제시되었다. 그렇기 때문에 이 부분에서 오류가 발생한 것이다.

해결하기

if(passwordEncoder.matches(resetPasswordRequestDto.getPassword(), user.get().getPassword())) {
	...
}

위 코드와 같이, matches 함수에 맞춰 인코딩 되지 않은 비밀번호, 인코딩 된 비밀번호 순으로 값을 변경해 주었고, 그 결과 오류 없이 잘 작동되었다.

해결하기 전에는 "대체 뭘 잘 못한거지.."라는 생각이었는데.. 막상 문제점을 찾고나니 너무 간단해서 허탈했다 ㅋㅋ.... 다음부터는 주의해서 개발하도록 다짐했다.