Переопределение обработчика ошибок по умолчанию в spring -boot - PullRequest
1 голос
/ 06 августа 2020

Как я могу переопределить вывод обработки ошибок Spring Boot по умолчанию? Когда происходит ответ 403 или что-то подобное, я хочу изменить отображаемое по умолчанию.

Прямо сейчас у меня есть класс, который расширяет OncePerRequestFilter, добавленный перед UsernamePasswordAuthenticationFilter цепочки фильтров. В моем настраиваемом фильтре я проверяю, истек ли токен 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 голосов
/ 06 августа 2020

Вместо того, чтобы устанавливать статус ответа в фильтре, я бы порекомендовал вам создать собственное исключение, например - TokenExpiredException, и добавить его в блок if вашего кода -

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

Затем вы можете создать a @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 - это класс, представляющий ваше настраиваемое ответное сообщение.

...