Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to gracefully handle exceptions in Spring Security not handled by ControllerAdvice?

I have recently implemented Spring Security in my Spring 4 / Hibernate Web application to handle logging in/out and different user roles.

After a lot of reading it appears to work pretty fine now, but I noticed that exceptions thrown due to a wrong Spring Security configuration were not handled gracefully using my custom handler but shown as an ugly Tomcat error page (showing HTTP Status 500 - UserDetailsService is required followed by a stacktrace).

Solving the particular error was not difficult (adding userDetailsService(userDetailsService) in the RememberMe configuration) but the fact remains that some exceptions thrown are not handled by the ControllerAdvice shown below handling MaxUploadSizeExceededException and all other runtime exceptions:

@ControllerAdvice
public class ExceptionHandlingControllerAdvice {

public static final String DEFAULT_ERROR_VIEW = "genericerror";

@ExceptionHandler(value = MaxUploadSizeExceededException.class)
public View maxUploadSizeExceededExceptionHandler(
        HttpServletRequest req) throws IOException {

    String redirectUrl = req.getRequestURL().toString();

    RedirectView rv = new RedirectView(redirectUrl);

    FlashMap outputFlashMap = RequestContextUtils.getOutputFlashMap(req);
    if (outputFlashMap != null) {
        outputFlashMap.put(KeyConstants.FLASH_ERROR_KEY, "Bestand is te groot");
    }
    return rv;
}

@ExceptionHandler(value = RuntimeException.class)
public View defaultErrorHandler(HttpServletRequest req, Exception e) {

    RedirectView rv = new RedirectView("/error", true); //context relative

    StackTraceElement[] steArray = e.getStackTrace();
    StringBuilder stackTrace = new StringBuilder();
    for (StackTraceElement element: steArray) {
        stackTrace.append(element.toString() + "\n");
    }

    FlashMap outputFlashMap = RequestContextUtils.getOutputFlashMap(req);
    if (outputFlashMap != null) {
        outputFlashMap.put("url", req.getRequestURL());
        outputFlashMap.put("excClassName", e.getClass().getName());
        outputFlashMap.put("excMessage", e.getMessage());
        outputFlashMap.put("excStacktrace", stackTrace.toString());
    }
    e.printStackTrace();

    return rv;
}
}

But the exception thrown by the incomplete configured Security is probably not caught by this mechanism because the login POST request is intercepted by Spring Security before any controller method is called. I would like to show ALL exceptions in graceful way on a custom error page, also the ones thrown before a Controller comes into place.

I cannot find much information about that, all error handling techniques described in the Spring manual (http://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#mvc-exceptionhandlers) seems to use a Controller advice.

Is there a convenient way to handle ALL exceptions in a generic way? And makes that my Controller advice class to handle exceptions superfluous?

like image 331
klausch Avatar asked Mar 18 '16 17:03

klausch


People also ask

How do you handle security exceptions in spring?

Spring security exceptions can be directly handled by adding custom filters and constructing the response body. To handle these exceptions at a global level via @ExceptionHandler and @ControllerAdvice, we need a custom implementation of AuthenticationEntryPoint.

How many ways can you handle exceptions in Spring MVC?

Our goal is to not handle exceptions explicitly in Controller methods where possible. They are a cross-cutting concern better handled separately in dedicated code. There are three options: per exception, per controller or globally. http://github.com/paulc4/mvc-exceptions.

How do you handle exceptions in a Spring web application?

Spring Exception Handling - Custom Exception Class Notice that we can use @ResponseStatus annotation with exception classes to define the HTTP code that will be sent by our application when this type of exception is thrown by our application and handled by our exception handling implementations.


1 Answers

As you have noticed, @ExceptionHandler does not not work for an exception that is thrown outside (lower in the heap than) Spring MVC.

You can catch all exceptions not caught elsewhere by specifying an error page in web.xml:

<error-page>
  <exception-type>java.lang.Throwable</exception-type>
  <location>/500</location>
</error-page>

You make this 500 page as a normal page, typically in Spring MVC:

@RequestMapping(value="/500")
public @ResponseBody String handleException(HttpServletRequest req) {

    // this will get the exception thrown/caught
    Throwable exception = (Throwable)req.getAttribute("javax.servlet.error.exception");

    // customize the response as you want
    return "Internal server error.";
}
like image 181
holmis83 Avatar answered Nov 07 '22 06:11

holmis83