2016-03-18 13 views
13

Ho recentemente implementato Spring Security nell'applicazione Web Spring 4/Hibernate per gestire l'accesso/uscita e diversi ruoli utente.Come gestire con garbo le eccezioni in Spring Security non gestite da ControllerAdvice?

Dopo molte letture sembra funzionare abbastanza bene ora, ma ho notato che le eccezioni lanciate a causa di una errata configurazione di Spring Security non sono state gestite correttamente usando il mio gestore personalizzato ma mostrate come una brutta pagina di errore di Tomcat (che mostra lo stato HTTP 500 - È richiesto UserDetailsService seguito da uno stacktrace).

Risolvendo l'errore particolare non era difficile (aggiungendo userDetailsService (userDetailsService) nella configurazione RememberMe), ma rimane il fatto che alcune eccezioni generate non sono gestiti dal ControllerAdvice mostrate sotto movimentazione MaxUploadSizeExceededException e tutte le altre eccezioni runtime:

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

Ma l'eccezione generata dalla Sicurezza configurata incompleta probabilmente non viene catturata da questo meccanismo perché la richiesta POST di accesso viene intercettata da Spring Security prima che venga chiamato qualsiasi metodo del controller. Vorrei mostrare TUTTE le eccezioni in modo aggraziato su una pagina di errore personalizzata, anche quelle lanciate prima che un Controller entri in posizione.

Non riesco a trovare molte informazioni al riguardo, tutte le tecniche di gestione degli errori descritte nel manuale Spring() sembrano utilizzare un consiglio del controller.

Esiste un modo conveniente per gestire TUTTE le eccezioni in modo generico? E rende superflua la mia classe di consigli Controller per gestire le eccezioni?

+0

Perché non è possibile modificare semplicemente @ExceptionHandler (value = RuntimeException.class) in @ExceptionHandler (value = Exception.class). In questo modo dovresti essere in grado di gestire tutte le eccezioni. – JChap

+1

No, questo non risolve il problema. Il problema non è che la classe di eccezione sia troppo specifica ma che l'eccezione venga lanciata prima che venga chiamato un metodo Controller e che ControllerAdvice entri in gioco. – klausch

+0

Ok Gotcha, Potrebbe essere che tu vai a un livello basso come usare il gestore delle eccezioni del servlet. – JChap

risposta

8

Come si è notato, @ExceptionHandler non funziona per un'eccezione che viene generata all'esterno (inferiore nell'heap rispetto a) Spring MVC.

È possibile catturare tutte le eccezioni non catturate altrove specificando una pagina di errore in web.xml:

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

Si effettua questa pagina 500 come una pagina normale, in genere 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."; 
} 
+0

Grazie per questo consiglio, sembra logico, ma non uso web.xml. La mia applicazione è completamente configurata con classi @Configuration basate su Java. Proverò a capire come tradurre l'elemento error-page in Java config. – klausch

3

Se si utilizza Spring Boot, è possibile creare un ErrorController personalizzato, c'è un predefinito denominato BasicErrorController già a portata di mano.