1

I'm developing a web-app using SpringBoot. Everything is going well, but I have a little problem:

I cannot catch an exception. A custom one:

public class BadCredentialsException extends RuntimeException {
    public BadCredentialsException(String message){
        super(message);
    }
}

I want to catch it in a OncePerRequestFilter, this one:

@Slf4j
public class AuthExceptionHandlerFilter extends OncePerRequestFilter {

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        log.debug("AuthExceptionFilter");
        try {
            filterChain.doFilter(request, response);
        } catch (BadRequestAuthException e) {
            log.info("Bad auth request received: {}", e.getMessage());
            response.sendError(HttpServletResponse.SC_BAD_REQUEST, e.getMessage());

        //===========HERE==========
        } catch (BadCredentialsException ex) {
            log.info("Bad credentials: {}", ex.getMessage());
            response.sendError(HttpServletResponse.SC_UNAUTHORIZED, ex.getMessage());
        }
    }
}

I have an AuthFilter too, but it's unrelated. And a DefaultExceptionHandler, what catches everything:

@Slf4j
public class DefaultExceptionHandlerFilter extends OncePerRequestFilter {
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        try{
            log.debug("DefaultExceptionHandler");
            filterChain.doFilter(request, response);
        }catch(RuntimeException e){
            log.warn("Internal server error: {}", e.getMessage());
            e.printStackTrace();
            response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
        }catch (Exception e){
            log.error("Unknown exception: {}", e.getMessage());
            e.printStackTrace();
            response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
        }
    }
}

All the filters are configured by this:

@Configuration
public class FilterConfig {

    @Bean
    public FilterRegistrationBean<DefaultExceptionHandlerFilter> exceptionHandlerFilterBean(){
        FilterRegistrationBean<DefaultExceptionHandlerFilter> filter = new FilterRegistrationBean<>();
        filter.setFilter(defaultExceptionFilter());
        filter.setOrder(Ordered.HIGHEST_PRECEDENCE);
        return filter;
    }

    @Bean
    public DefaultExceptionHandlerFilter defaultExceptionFilter(){
        return new DefaultExceptionHandlerFilter();
    }

    @Bean
    public FilterRegistrationBean<AuthExceptionHandlerFilter> authExceptionHandlerFilterFilterBean(){
        FilterRegistrationBean<AuthExceptionHandlerFilter> filter = new FilterRegistrationBean<>();
        filter.setFilter(authExceptionHandlerFilter());
        filter.setOrder(1);
        return filter;
    }

    @Bean
    public AuthExceptionHandlerFilter authExceptionHandlerFilter(){
        return new AuthExceptionHandlerFilter();
    }

    @Bean
    public FilterRegistrationBean<AuthFilter> authFilterBean (AccessTokenService authService){
        FilterRegistrationBean<AuthFilter> authFilter = new FilterRegistrationBean<>();
        authFilter.setFilter(loginFilter(authService));
        authFilter.setOrder(2);
        return authFilter;
    }

    @Bean
    public AuthFilter loginFilter(AccessTokenService authService){
        return new AuthFilter(authService);
    }
}

The following method throws the exception: (Called by a service, what is called by the @RestController)

private SkyXpUser getAuthenticatedUser(LoginRequest loginRequest){
    SkyXpUser user = userService.getUserByName(loginRequest.getUserName());
    if(user == null){
        throw new BadCredentialsException("User cannot be found. Username: " + loginRequest.getUserName());
    }

    if(!user.getPassword().equals(loginRequest.getPassword())){
        throw new BadCredentialsException("Password is incorrect.");
    }
    return user;
}

And i get the following stack trace, when I enter an username what does not exists, or wrong email:

2018-05-29 19:23:16.841 ERROR 8924 --- [nio-8080-exec-3] s.filter.DefaultExceptionHandlerFilter   : Unknown exception: Request processing failed; nested exception is skyxplore.auth.domain.exception.BadCredentialsException: User cannot be found. Username: asad
org.springframework.web.util.NestedServletException: Request processing failed; nested exception is skyxplore.auth.domain.exception.BadCredentialsException: User cannot be found. Username: asad
    at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:982)
    at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:877)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:661)
    at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:851)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:742)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
    at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)

    at skyxplore.filter.AuthFilter.doFilterInternal(AuthFilter.java:51)

    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)

    at skyxplore.filter.AuthExceptionHandlerFilter.doFilterInternal(AuthExceptionHandlerFilter.java:22)

    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
    at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:99)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
    at org.springframework.web.filter.HttpPutFormContentFilter.doFilterInternal(HttpPutFormContentFilter.java:109)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
    at org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:81)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
    at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:200)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)

    at skyxplore.filter.DefaultExceptionHandlerFilter.doFilterInternal(DefaultExceptionHandlerFilter.java:19)

    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:198)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96)
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:496)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:140)
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:81)
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:87)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:342)
    at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:803)
    at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66)
    at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:790)
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1468)
    at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
    at java.lang.Thread.run(Thread.java:748)

Caused by: skyxplore.auth.domain.exception.BadCredentialsException: User cannot be found. Username: asad
    at skyxplore.auth.service.AccessTokenService.getAuthenticatedUser(AccessTokenService.java:46)
    at skyxplore.auth.service.AccessTokenService.login(AccessTokenService.java:31)
    at skyxplore.auth.controller.LoginController.login(LoginController.java:26)

    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:209)
    at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:136)
    at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:102)
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:877)
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:783)
    at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)
    at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:991)
    at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:925)
    at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:974)
    ... 53 more

The stack trace says All the 3 filters were called, and the DefaultExceptionHandler's "Exception" catch part catched the exception, not the "RuntimeException" one.

While the AuthFilter's "BadCredentialsException" part should catch this exception, once it is a BadCredentialsException. Or the DefaultExceptionHandler's "RuntimeException" part, because BadCredentialsException extends RuntimeException.

What the hack is happening here? How can I solve?

1 Answer 1

1

If i understand it right, you try to handle a exception from a controller. You need something that handle the exception from your controller.

GlobalHandler:

@ControllerAdvice
public class MyExceptionHandler {



@ExceptionHandler(value = BadCredentialsException.class)
public ResponseEntity handleAllExceptions(BadCredentialsException ex){
    //DO Something
    return new ResponseEntity("Error", HttpStatus.INTERNAL_SERVER_ERROR);
}
}

There are different ways see: https://spring.io/blog/2013/11/01/exception-handling-in-spring-mvc or What are the advantages of @ControllerAdvice over @ExceptionHandler or HandlerExceptionResolver for handling exceptions?

Sign up to request clarification or add additional context in comments.

3 Comments

Well, It's good for a solution, but I still not understand, why mine one does not work :D Thank you, I'll use these advices.
the problem is that your exception is wrapped in a NestedServletException thats why the "exception" part gets the exception ... its not an Runtimeexception and not your own type of exception ... after wrapping its an NestedServletException docs.spring.io/spring-framework/docs/current/javadoc-api/org/…
Second line, in the stack trace... Why I had not read about it before?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.