Spring boot 覆盖弹簧靴';的默认错误处理程序

Spring boot 覆盖弹簧靴';的默认错误处理程序,spring-boot,http-status-codes,http-error,Spring Boot,Http Status Codes,Http Error,如何覆盖Spring Boot错误处理的默认输出?当出现403响应或类似情况时,我想更改显示的默认值 现在,我在过滤器链的UsernamePasswordAuthenticationFilter之前添加了一个扩展了OncePerRequestFilter的类。在我的自定义筛选器中,我检查JWT令牌是否在doFilterInternal方法中过期 如果已过期,我将状态设置为403response.setStatus(HttpStatus.SC_禁止)和我写的内容 protected voi

如何覆盖Spring Boot错误处理的默认输出?当出现403响应或类似情况时,我想更改显示的默认值

现在,我在过滤器链的UsernamePasswordAuthenticationFilter之前添加了一个扩展了OncePerRequestFilter的类。在我的自定义筛选器中,我检查JWT令牌是否在
doFilterInternal
方法中过期

如果已过期,我将状态设置为403
response.setStatus(HttpStatus.SC_禁止)和我写的内容

    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) {

        ...


        if (jwtTokenUtil.isTokenExpired(jwtToken)) {
                response.setStatus(HttpStatus.SC_FORBIDDEN);
                response.setContentType(MediaType.APPLICATION_JSON_VALUE);
                response.getWriter().write(error.toString());
                response.getWriter().flush();
                response.getWriter().close();
        }

        ...

    }
如果我设置了状态代码而不编写内容,则不会出现异常,用户会获得正确的状态代码,但内容是由Spring自动创建的

如果我设置了状态码并编写了内容,那么从用户的角度来看,一切实际上都是正常的,但是在内部出现了一个异常,它谈论的是响应是如何被写入的

我想用正确的方式做事;可能有一些类需要覆盖和自定义,以便我可以根据错误代码自定义内容,但我一直无法找到有关这些内容的任何信息

编辑: 如果我试图写入主体,这是内部抛出的异常

Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Unable to handle the Spring Security Exception because the response is already committed.] with root cause

org.springframework.security.access.AccessDeniedException: Access is denied
    at org.springframework.security.access.vote.AffirmativeBased.decide(AffirmativeBased.java:84) ~[spring-security-core-5.3.3.RELEASE.jar:5.3.3.RELEASE]
    at org.springframework.security.access.intercept.AbstractSecurityInterceptor.beforeInvocation(AbstractSecurityInterceptor.java:233) ~[spring-security-core-5.3.3.RELEASE.jar:5.3.3.RELEASE]
    at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:123) ~[spring-security-web-5.3.3.RELEASE.jar:5.3.3.RELEASE]
    at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:90) ~[spring-security-web-5.3.3.RELEASE.jar:5.3.3.RELEASE]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) ~[spring-security-web-5.3.3.RELEASE.jar:5.3.3.RELEASE]
    at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:118) ~[spring-security-web-5.3.3.RELEASE.jar:5.3.3.RELEASE]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) ~[spring-security-web-5.3.3.RELEASE.jar:5.3.3.RELEASE]
    at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:137) ~[spring-security-web-5.3.3.RELEASE.jar:5.3.3.RELEASE]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) ~[spring-security-web-5.3.3.RELEASE.jar:5.3.3.RELEASE]
    at org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:111) ~[spring-security-web-5.3.3.RELEASE.jar:5.3.3.RELEASE]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) ~[spring-security-web-5.3.3.RELEASE.jar:5.3.3.RELEASE]
    at org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:158) ~[spring-security-web-5.3.3.RELEASE.jar:5.3.3.RELEASE]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) ~[spring-security-web-5.3.3.RELEASE.jar:5.3.3.RELEASE]
    at org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:63) ~[spring-security-web-5.3.3.RELEASE.jar:5.3.3.RELEASE]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) ~[spring-security-web-5.3.3.RELEASE.jar:5.3.3.RELEASE]
    at com.abc.web.config.JwtRequestFilter.doFilterInternal(JwtRequestFilter.java:130) ~[classes/:na]

与其在过滤器中设置响应状态,我建议您创建一个自定义异常,如-
TokenExpiredException
,并将其抛出代码中的if块-

if (jwtTokenUtil.isTokenExpired(jwtToken)) {
    // ... some code ... //
    throw new TokenExpiredException("Token Expired");
    // ... some code ... //        
}
然后您可以创建一个
@ControllerAdvice
,它可以作为处理所有异常的中心类。准则中将明确区分关注点。同样可以通过以下方式完成-

您的
应用程序.properties
文件应禁用默认处理程序-

spring.mvc.throw-exception-if-no-handler-found=true
然后可以创建一个类来处理异常-

@RestControllerAdvice
public class GlobalControllerExceptionHandler extends ResponseEntityExceptionHandler {
    
    @ResponseBody
    @ResponseStatus(HttpStatus.FORBIDDEN)
    @ExceptionHandler(value = {TokenExpiredException.class})
    public ApiResponse handleTokenExpiredException(TokenExpiredException ex, WebRequest request) {
        return new ApiResponse(ex.getMessage());
    }
}

在这里,
ApiResponse
是一个表示自定义响应消息的类。

我建议您创建一个自定义异常,如-
TokenExpiredException
并将其抛出代码中的if块,而不是在筛选器中设置响应状态-

if (jwtTokenUtil.isTokenExpired(jwtToken)) {
    // ... some code ... //
    throw new TokenExpiredException("Token Expired");
    // ... some code ... //        
}
然后您可以创建一个
@ControllerAdvice
,它可以作为处理所有异常的中心类。准则中将明确区分关注点。同样可以通过以下方式完成-

您的
应用程序.properties
文件应禁用默认处理程序-

spring.mvc.throw-exception-if-no-handler-found=true
然后可以创建一个类来处理异常-

@RestControllerAdvice
public class GlobalControllerExceptionHandler extends ResponseEntityExceptionHandler {
    
    @ResponseBody
    @ResponseStatus(HttpStatus.FORBIDDEN)
    @ExceptionHandler(value = {TokenExpiredException.class})
    public ApiResponse handleTokenExpiredException(TokenExpiredException ex, WebRequest request) {
        return new ApiResponse(ex.getMessage());
    }
}

这里,
ApiResponse
是一个表示自定义响应消息的类。

是的,我目前有一个@ControllerAdvice类可以捕获异常。我尝试在过滤器中抛出一个自定义异常类,并将其添加到我的exceptionhandler中。它没有被抓住。我想我以前试过,但也失败了,这就是为什么我尝试了另一条路线。我猜ControllerAdvice或RestControllerAdvice只处理通过控制器的异常,而此异常在到达该点之前发生。是的!!我的回答没有用。在这方面还有其他一些讨论,似乎很有希望。我会自己尝试一种方法,稍后再讨论。我想这就是我需要的:。它说:
要完全替换默认行为,您可以实现ErrorController并注册该类型的bean定义,或者添加ErrorAttributes类型的bean以使用现有机制但替换内容。
,但我不知道它说“…实现ErrorController…”后的含义是的,我目前有一个@ControllerAdvice类可以捕获异常。我尝试在过滤器中抛出一个自定义异常类,并将其添加到我的exceptionhandler中。它没有被抓住。我想我以前试过,但也失败了,这就是为什么我尝试了另一条路线。我猜ControllerAdvice或RestControllerAdvice只处理通过控制器的异常,而此异常在到达该点之前发生。是的!!我的回答没有用。在这方面还有其他一些讨论,似乎很有希望。我会自己尝试一种方法,稍后再讨论。我想这就是我需要的:。它说:
要完全替换默认行为,您可以实现ErrorController并注册该类型的bean定义,或者添加ErrorAttributes类型的bean以使用现有机制但替换内容。
,但我不知道它说“…实现ErrorController…”后的含义