본문 바로가기

Projects/Monkey Penthouse

에러 표준화 과정 3 - 인증 과정에서 발생하는 Exception 처리

우리 프로젝트에서 처리되는 인증 방식은 세 가지이다.

  • 이메일과 비밀번호 검증을 통한 로그인
  • 타 소셜 플랫폼에서 대신 인증 
  • JWT 토큰 검증을 통한 인증

타 소셜 플랫폼에서 발생하는 에러는 해당 플랫폼 서버에서 잘 처리되어 에러 응답을 보내주며 우리는 그것을 받아 SocialLoginFailedException으로 처리하고 있기 때문에 여기서는 따로 언급하지는 않겠다.

 

다만 Spring Security 기능을 사용하고 있는 이메일/비밀번호 검증과 JWT 토큰 검증 기능에서 발생하는 에러는 별도의 처리 방식이 필요했다.

 

JWT 토큰 검증 과정에서 발생하는 에러 처리

원래는 유효하지 않은 토큰을 검증할 때에는 발생하는 jwt 검증 에러를 모두 catch하고 그냥 authentication을 SecurityContext에 저장하지 않는다.

그리고 AuthenticationEntryPoint를 구현하여 authentication이 SecurityCotext에 없을 때 에러 처리를 해주고 있다.

따라서 JWT 토큰이 기간이 만료된 것인지, 혹은 잘못된 형식인지 등의 구체적인 에러 원인을 알 수가 없다. 

그러나 여기서 토큰 검증 과정에서 발생한 JWT 관련 에러가 DispatcherServlet 까지도 닿지 못하기 때문에 이 Exception들을 그대로 처리해줄 수 있는 방법도 없다.

 

이 때 똑같은 문제를 해결한 블로그 글이 있어 이를 참조하여 유사하게 해결했다.

Spring Security JWT 토큰 검증 시 Exception 예외 처리
 

Spring Security JWT 토큰 검증 시 Exception 예외 처리

Spring Security 예외 Spring Security에서 토큰을 검증할 경우, 예외가 발생한다면 기존에 사용 중이던 Custom Exception으로 처리가 될까? 그러면 편하긴 하겠지만 그건 안될 말이지^^ 🙃아니🙃 왜 안되는

beemiel.tistory.com

Exception을 보낼 수는 없지만 AuthenticationEntryPoint의 commence() 메소드에서는 request 객체를 파라미터로 받고 있으니, 토큰 검증 과정에서 request에 에러 정보를 담으면 이것을 commence() 안에서 열어봄으로써 어떤 원인으로 토큰 검증이 실패했는지 알 수 있는 것이다.

 

이를 통해서 토큰 검증시 발생하는 jwonwebtoken에서 정의된 각각의 Exception을 구별하고 이를 통해 어떤 이유로 검증에 실패했는지 응답 메시지로 보낼 수 있게 되었다.


이메일/비밀번호 검증 과정에서 발생하는 에러 처리

이 과정에서 에러 처리를 할 때의 목표는 이메일/비밀번호 둘 중 검증 실패 원인을 특정하고자 하는 것이었다.

즉 로그인 실패 응답을

  • "존재하지 않는 회원의 이메일입니다."
  • "비밀번호가 일치하지 않습니다."

이렇게 두 케이스로 나누고자 했다.

 

2022.01.04 - [Projects/Monkey Penthouse] - Spring Security를 이용한 JWT 로그인 2 - 구현 과정

 

Spring Security를 이용한 JWT 로그인 2 - 구현 과정

목표 로그인 요청 시, 이메일과 비밀번호를 검증하여 JWT 발급 '/user/all/*'을 제외한 모든 요청에 대하여 JWT 인증/인가 처리  참고: https://bcp0109.tistory.com/301 Spring Security 와 JWT 겉핥기 Introduc..

eungeun506.tistory.com

저번 글에서 작성했던 것처럼 UserDetailService 구현체에서 loadUserByUsername()을 통해 이메일 검증을 하다가 실패하게 되면 UsernameNotFoundException을 던지고, DaoAuthenticationProvider에서의 additionalAuthenticationChecks()에서 비밀번호 검증이 실패하면 BadCredentialsException을 던진다. 

 

그래서 던져지는 두 Exception 정보만 구별해서 처리하면 되겠지 싶었는데, 이메일이 틀린 케이스에서도 자꾸 BadCredentialsException만 들어왔다. 이유는 다음 블로그 글과 같았다.

https://wildeveloperetrain.tistory.com/56

 

UsernameNotFoundException Not Working 이유 파헤치기

Spring security, JWT 로그인 구현 중 UsernameNotFoundException 처리가 안 되는 현상이 발생하였습니다. 분명 해당 Exception 이 발생하지만 최종적으로는 BadCredentialsException 으로 처리되어 최종 respons..

wildeveloperetrain.tistory.com

DaoAuthenticationProvider에서 hideUserNotFoundExceptions 설정이 디폴트로 True로 있던 것이 문제였다.

authenticate() 메서드를 통해 인증을 진행하다가 UserNotFoundException이 발생하더라도 이를 숨기는 hideUserNotFoundExceptions이 True로 되어있으면 UserNotFoundException을 catch해서 대신 BadCredentialsExeption을 보내주고 있었다.

 

위의 글에서는 hideUserNotFoundExceptions을 false로 설정하지 않고, 인증 전에 DB를 한 번더 조회함으로써 이를 처리했지만 나는 성능이 걱정되기도 하고, 차피 이 검증 로직을 거치는 요청은 이 로그인 하나뿐이므로 DaoAuthenticationProvider의 hideUserNotFoundExceptions 값을 false로 설정하여 문제를 해결하였다.