【问题标题】:How to gracefully handle exceptions in Spring Security not handled by ControllerAdvice?如何优雅地处理 Spring Security 中未由 ControllerAdvice 处理的异常?
【发布时间】:2023-03-21 20:35:01
【问题描述】:

我最近在我的 Spring 4 / Hibernate Web 应用程序中实现了 Spring Security 来处理登录/注销和不同的用户角色。

经过大量阅读,它现在似乎工作得很好,但我注意到由于错误的 Spring Security 配置引发的异常没有使用我的自定义处理程序优雅地处理,而是显示为丑陋的 Tomcat 错误页面(显示 HTTP 状态 500 - 需要 UserDetailsS​​ervice 后跟堆栈跟踪)。

解决特定错误并不困难(在 RememberMe 配置中添加 userDetailsS​​ervice(userDetailsS​​ervice))但事实仍然是抛出的一些异常没有由 如下所示的 ControllerAdvice 处理 MaxUploadSizeExceededException 和所有其他运行时异常:

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

但是配置不完整的Security抛出的异常很可能没有被这个机制捕获,因为登录POST请求在调用任何控制器方法之前就被Spring Security拦截了。我想在自定义错误页面上以优雅的方式显示所有异常,以及在控制器到位之前抛出的异常。

我找不到太多相关信息,Spring 手册 (http://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#mvc-exceptionhandlers) 中描述的所有错误处理技术似乎都使用了控制器建议。

有没有一种方便的方法来以通用方式处理所有异常?并让我的 Controller 建议类处理异常变得多余?

【问题讨论】:

  • 为什么不能简单地将@ExceptionHandler(value = RuntimeException.class) 改为@ExceptionHandler(value = Exception.class)。这样你应该能够处理所有的异常。
  • 不,这并不能解决问题。问题不在于异常类过于具体,而是在调用 Controller 方法和 ControllerAdvice 发挥作用之前抛出异常。
  • 好的,明白了,你可能会像使用 servlet 异常处理程序那样低级。

标签: spring-mvc spring-security error-handling


【解决方案1】:

如果你使用 Spring Boot,你可以创建一个自定义的 ErrorController,你手边已经有一个名为 BasicErrorController 的默认控制器。

【讨论】:

    【解决方案2】:

    如您所见,@ExceptionHandler 不适用于在 Spring MVC 之外(堆中低于)抛出的异常。

    您可以通过在 web.xml 中指定错误页面来捕获其他地方未捕获的所有异常:

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

    您将这个 500 页面作为普通页面,通常在 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.";
    }
    

    【讨论】:

    • 感谢您的建议,听起来合乎逻辑,但我不使用 web.xml。我的应用程序完全配置了基于 Java 的 @Configuration 类。我将尝试弄清楚如何将错误页面元素转换为 Java 配置。
    • @klausch:您是否使用基于/不使用 xml 的注释来解决这个问题?
    • @sarwar026 我不再处理这个应用程序了,帖子已经 3 岁了。使用 Spring Boot 和错误控制器可能是可能的,但我没有尝试过。请参阅下面的帖子。
    猜你喜欢
    • 2019-06-04
    • 1970-01-01
    • 2014-07-10
    • 2017-03-04
    • 2015-12-15
    • 2017-11-23
    • 2015-12-14
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多