Search
👊🏻

로그인 에러 분기 과정

Person
태그
에러 분기

로그인 에러 커스텀 (Spring Security)

문제 상황

문제 상황은 다음과 같다.
프론트 팀에서 로그인 시 이메일과 비밀번호가 일치하지 않았을 때 각각에 대한 에러 분기 처리를 요청하였다.
계속되는 에러 분기의 늪..!!
우리 프로젝트에서는 exception 패키지 안에서 에러 분기를 처리하고
인증과 인가 관련한 에러는 AuthenticationEntryPoint의 구현체인 CustomAuthenticationEntryPoint에서 처리한다.
AuthenticationEntryPoint란?
인증 처리 과정에서 예외를 핸들링하는 인터페이스로 인증되지 않은 요청(AuthenticationException)인 경우 이를 사용하여 처리한다.
Spring Security에 의한 차단은 일반적으로 사용하는 에러 처리기로는 처리할 수 없다.
예를 들어, 프로젝트에서 인증 인가 후의 일반적인 에러는 다음과 같이 ApiException으로 처리한다.
if (groupMembers.stream().noneMatch(gm -> gm.getEmail().equals(currentEmail))) { throw new ApiException(ErrorType.USER_NOT_AUTHORIZED); }
Java
복사
ApiException으로 에러를 던지면 프론트에서는 기존에 커스텀한 에러코드와 메세지를 JSON 형식으로 확인할 수 있다.
그러나!
시큐리티 에러의 경우 서블릿 필터 단계에 속하기 때문에 JSON 응답을 커스텀하기 위해서는 별도의 설정이 필요하다.
만약 이 설정을 변경하지 않고 에러를 내려준다면 모든 요청에 대해서 공통으로 아래의 401 응답만을 받게 될 것이다.
실제로 이것 때문에 아~~주 답답했다.
401 Unauthorized
즉, 커스텀한 에러코드와 메세지롤 프론트로 내려주지 못하기 때문에 프론트에서는 상황에 맞는 에러 분기 처리를 할 수가 없게 된다.

해결 방법

인증 예외가 발생했을 때의 응답 커스텀을 위해 AuthenticationEntryPoint 에서
이메일이 일치하지 않을 때와 비밀번호가 일치하지 않을 때 던져주는 각각의 에러 객체를 확인하여 instanceof를 사용하여 이 객체가 spring security가 던져주는 AuthenticationException 을 상속 받고 있는지 확인하면 된다.
spring security는 인증과 인가 예외 상황에 대해 다양한 에러를 알아서 던져주는데
이메일이 일치하지 않을 때는 UsernameNotFoundException 를 비밀번호가 일치하지 않을 때는 BadCredentialsException 을 던져준다.
그리고 에러 객체가 인증 예외 클래스를 상속받고 있음이 확인된다면 기존에 만들어 놓았던
내려주고자 했던 커스텀 에러 메세지를 담아 JSON 형태로 반환하기 위해 별도의 Response 메서드를 만들어 보내준다.
private void setResponse(HttpServletResponse response, ErrorType errorType) throws IOException { response.setContentType("application/json;charset=UTF-8"); int status = Integer.parseInt(String.valueOf(errorType.getStatus()).substring(0,3)); response.setStatus(status); response.getWriter().println( "{\"status\" : \"" + errorType.getStatus() + "\"," + "\"errorCode\" : \"" + errorType.getErrorCode() + "\"," + " \"message\" : \"" + errorType.getMessage() + "\"}"); }
Java
복사

한 걸음 더

만약 Security에 AuthenticationEntryPoint 를 등록하지 않았을 경우(그리고 필터 체인의 순서가 어디인지에 따라서도 달라진다.)
인증 예외임에도 403 에러가 발생할 것이다.
왜냐고? spring security에서는 그럴 경우 Http403ForbiddenEntryPoint 를 사용하기 때문이다.
우리 프로젝트처럼 spring security가 제공하는 자체적인 인증서비스를 사용하고 있지 않고 별도의 로그인 로직을 사용하고
AuthenticationEntryPoint 를 등록하지 않았을 경우 인증 예외에도 403 에러가 던져진다는 점 꼭 기억하자!

고민해볼 점

사용자 접근성 vs 보안 강화
프론트 팀의 요청에 따라 로그인 에러 분기는 해결을 했다.
그런데 여러 자료를 찾아보다 보안을 위해서는 아이디와 비밀번호가 틀린 경우 모두 다 BadCredentialsException 로 에러를 처리하는 것이 좋다고 한다.
사용자 관점에서 고려해보자면 로그인 에러 분기를 그대로 사용할 경우 사용자는 자신이 왜 로그인을 할 수 없는지 메세지를 통해 명확하게 전달 받을 수 있으므로 매우 편리할 것이다.
그러나 혹시라도 탈취를 목적으로 접근한 사람이 그토록 명확한 메세지를 받게 된다면? 보안 상 좋지 않을 것임은 분명하다.