API 서버를 만들면서, 비밀번호는 평문으로 저장하지 않고 BcryptEncoder
를 적용했다.
회원가입 과정에서도 비밀번호를 db에 encode에서 저장하고, 유저인증이 필요할때마다 엔티티 조회를 위해 다음같은 코드를 썼다.
userRepository.findByEmailAndPassword(request.email, encoder.encode(request.password))
그런데, 분명 password와 email에 정상적으로 옳은 평문을 넣었는데도 계속 유저를 찾지 못했다!
이게 무슨일이지 싶어서 디버거를 돌려봤다.
테스트용으로 쓴 비밀번호는 평문으로 "password"였는데, 인코드 결과 다음과 같이 저장되어있다.
이제 디버거를 통해 같은 "password"를 encode한 결과를 보면
다르다.. 7번째부터 확연히 다르다. 난 분명 같은 평문을 넣었는데 이런 일이 왜 생겼을까?
이유는 솔트(저자 : yoosj97님)때문이다. 솔트란 간단히 말해 평문에 임의의 문자열을 붙여 암호화 하는 보완 방법이다.
따라서 encode마다 임의로 문자열이 붙어서 인코딩되니, 같을 수가 없었다.
내가 사용하는 Spring Security의 PasswordEncoder는, .matches
라는 함수를 제공한다. 두 평문 하나와 인코드된 문자열을 넘기면, 같은건지 체크해준다.
로직은 굉장히 복잡한것으로 보여서.. 따로 공부하거나 다루진 않겠다. 오버헤드가 얼마나 될지정도는 궁금하긴 하다.
passwordEncoder.matches(password, storedEncodedPassword)
나는 맨위에 첨부한 코드처럼, 평문을 쓸땐 그냥 findByEmailAndPassword를 썼다.
근데 이제 db에 있는 Password를 가져와 matches로 비교를 해야하기에, 과정을 쪼개야한다.
- findByEmail 등으로, 아이디등의 정보만 가지고 엔티티를 조회한다. 이 떄, Email등은 당연히 Unique해야한다.
- 방금 가져온 엔티티를 user라고 하자. 이후 passwordEncoder.matches를 통해 user.password(암호화된, db에 저장된것)와 평문을 비교한다.
- matches의 결과가 True면 인증 성공, False거나 user 자체가 null이 반환되었다면 인증 실패로 처리한다.