2016-03-18 23 views
13

Niedawno zaimplementowałem Spring Security w mojej aplikacji Spring 4/Hibernate Web do obsługi logowania/wylogowywania i różnych ról użytkowników.Jak sprawnie obsługiwać wyjątki w Spring Security, które nie są obsługiwane przez ControllerAdvice?

Po wielu lekturze wygląda na to, że działa całkiem dobrze, ale zauważyłem, że wyjątki generowane z powodu niewłaściwej konfiguracji Spring Security nie były obsługiwane z wdzięcznością za pomocą mojego niestandardowego programu obsługi, ale pokazywane jako brzydka strona błędu Tomcat (pokazująca stan HTTP 500 - Wymagana jest usługa UserDetailsService, a następnie ślad stosu).

Rozwiązywanie dany błąd nie było trudne (dodawanie userDetailsService (userDetailsService) w konfiguracji Twój na zawsze), ale pozostaje faktem, że pewne wyjątki rzucane nie są obsługiwane przez ControllerAdvice poniżej obsługi MaxUploadSizeExceededException i wszystkie inne wyjątki środowiska wykonawczego:

@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; 
} 
} 

Ale wyjątek zgłoszony przez nieskompletowane skonfigurowane zabezpieczenia prawdopodobnie nie jest przechwytywany przez ten mechanizm, ponieważ żądanie POST logowania jest przechwytywane przez Spring Security przed wywołaniem jakiejkolwiek metody kontrolera. Chciałbym pokazać WSZYSTKIE wyjątki w pełen wdzięku sposób na stronie błędów niestandardowych, również tych wyrzuconych przed uruchomieniem Kontrolera.

Nie mogę znaleźć wiele informacji na ten temat, wszystkie techniki obsługi błędów opisane w instrukcji Spring (http://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#mvc-exceptionhandlers) wydają się korzystać z porady kontrolera.

Czy istnieje wygodny sposób obsługi WSZYSTKICH wyjątków w sposób ogólny? I sprawia, że ​​moja klasa rada kontrolera do obsługi wyjątków jest zbędna?

+0

Dlaczego nie można po prostu zmienić @ExceptionHandler (value = RuntimeException.class) na @ExceptionHandler (value = Exception.class). W ten sposób powinieneś być w stanie obsłużyć wszystkie wyjątki. – JChap

+1

Nie, to nie rozwiązuje problemu. Problem nie polega na tym, że klasa wyjątków jest zbyt specyficzna, ale wyjątek jest generowany przed wywołaniem metody kontrolera i wchodzi w grę narzędzie ControllerAdvice. – klausch

+0

Ok Gotcha, może być wtedy niski poziom jak przy użyciu obsługi wyjątku serwletu. – JChap

Odpowiedz

8

Jak zauważyłeś, @ExceptionHandler nie działa dla wyjątku, który jest wyrzucany na zewnątrz (niższy w stercie niż) Spring MVC.

Można złapać wszystkie wyjątki złowione nie gdzie indziej, określając stronę błędu w web.xml:

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

dokonać tej 500 strony jako normalnej stronie, zwykle w 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."; 
} 
+0

Dzięki za radę, brzmi logicznie, ale nie używam web.xml. Moja aplikacja jest w pełni skonfigurowana z wykorzystaniem klas JavaConfiguration opartych na Javie. Postaram się dowiedzieć, jak przetłumaczyć element strony błędu na konfigurację Java. – klausch

3

Jeśli używasz Spring Boot, możesz utworzyć niestandardowy ErrorController, istnieje domyślny o nazwie BasicErrorController już na ciebie.