[Dining-together] Exception 처리

Jifrozen·2021년 5월 25일
0

Dining-together

목록 보기
4/25

Exception 처리

예외처리는 아주 중요하면서 까다로운 작업이다.
찾아본바로는 과할만큼 상세하고 다양하게 예외를 잡아 처리하는것이 좋다고 한다.
api 처리 중 특정한 exception이 발생할 경우 공통으로 처리하는 방법에 대해 알아보도록 하자.
spring은 이러한 처리를 위해 ControllerAdvice를 제공하고 있다.

@ControllerAdvice

@Controller나 @RestController에서 발생한 예외를 한 곳에서 관리하고 처리할 수 있게 도와주는 어노테이션이다.
@ControllerAdvice 와 @ExceptionHandler를 조합하여 예외 처리를 공통 코드로 분리하여 작성할 수 있다.



@RestControllerAdvice
@RequiredArgsConstructor
public class ExceptionAdvice {

    private final ResponseService responseService;

    @ExceptionHandler(Exception.class)
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    protected CommonResult defaultException(HttpServletRequest request,Exception e){
        return responseService.getFailResult(500,"실패");
    }

@RestControllerAdvice

@ControllerAdvice의 restapi 버전이라고 보면된다. json 형태 결과를 반환한다.

@ExceptionHandler

exception 이 발생하면 해당 handler로 처리하겠다고 명시해주는 어노테이션이다.

@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)

defaultException를 정의하기 때문에 가장 기본적인 서버오류 500을 내려가도록 설정했다.

return responseService.getFailResult(500,"실패");

이미 만든 responseservice를 통해 실패 api 메시지를 보낸다.


이제 내가 만들어준 exception을 등록해보자.

public class loginFailedException extends RuntimeException  {
    public loginFailedException(String msg, Throwable t) {
        super(msg, t);
    }

    public loginFailedException(String msg) {
        super(msg);
    }

    public loginFailedException() {
        super();
    }
}
    @ExceptionHandler(loginFailedException.class)
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    protected CommonResult loginFailedException(HttpServletRequest request, loginFailedException e) {
        return responseService.getFailResult(502,"로그인 실패했습니다..");
    }

이런식으로 오류를 만들어 설정해준다.


spring security 적용하며 생기는 문제점

위와같이 커스텀 예외처리가 적용되지 않는 경우

  1. Jwt 토큰 없이 api 호출
  2. 형식에 맞지 않거나 만료된 Jwt 로 api 호출
  3. Jwt 토큰으로 api 출하였으나 해당 리소스에 대한 권한이 없는 경우

왜...?
필터링 순서 때문이다. 스프링 시큐리티의 경우 spring 앞단에서 필터링을 진행하기 때문에 해당 예외가 spring 의 DispatcherServlet까지 도달하지 못한다.

해결책

1,2번 해결

온전한 Jwt 전달이 안되는 경우 토큰 인증 처리 자체가 불가능, 토크 검증부분에서 프로세스 종료
해당 예외를 잡기 위해선 스프링 시큐리티에서 제공하는 AuthenticationEntryPoint를 상속받아 재정의 한다.

@Component
@Slf4j
public class CustomAuthenticationEntryPoint implements AuthenticationEntryPoint {
    @Override
    public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException ex) throws IOException,
            ServletException {
        RequestDispatcher dispatcher = request.getRequestDispatcher("/exception/entrypoint");
        dispatcher.forward(request, response);
    }
}
@RequiredArgsConstructor
@RestController
@RequestMapping(value = "/exception")
public class ExceptionController {

    @GetMapping(value = "/entrypoint")
    public CommonResult entrypointException() {
        throw new AuthenticationEntryPointException();
    }

    @GetMapping(value = "/accessdenied")
    public CommonResult accessdeniedException() {
        throw new AccessDeniedException("");
    }
}

3번 해결

jwt 토큰이 정상이라는 가정하에 이 토큰을 가지고 접근하지 못하는 리소스에 접근했다는 에러를 처리해줘야한다. (사용자가 운영자의 리소스 접근같은 경우)

@Component
@Slf4j
public class CustomAccessDeniedHandler implements AccessDeniedHandler {

    @Override
    public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException exception) throws IOException, ServletException {
        RequestDispatcher dispatcher = request.getRequestDispatcher("/exception/accessdenied");
        dispatcher.forward(request, response);
    }
}

참고 자료

https://jeong-pro.tistory.com/195
https://daddyprogrammer.org/post/636/springboot2-springsecurity-authentication-authorization/

0개의 댓글