覆盖spring-boot的默认错误处理程序

时间:2020-08-06 19:41:30

标签: spring-boot http-status-codes http-error

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

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

如果它已过期,我将状态设置为403 response.setStatus(HttpStatus.SC_FORBIDDEN);并写入内容

    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]

1 个答案:

答案 0 :(得分:0)

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

if (jwtTokenUtil.isTokenExpired(jwtToken)) {
    // ... some code ... //
    throw new TokenExpiredException("Token Expired");
    // ... some code ... //        
}

然后,您可以创建一个@ControllerAdvice,它可以作为处理所有异常的中心类。代码中将明确分离关注点。可以通过以下方式完成此操作-

您的application.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是代表您的自定义响应消息的类。

相关问题