Поймать пользовательские исключения, созданные цепочкой безопасности Spring, и обработать их глобальным обработчиком ошибок - PullRequest
0 голосов
/ 02 апреля 2020

В настоящее время я нахожусь в процессе разработки REST-Api с помощью Springboot. После запуска и запуска моего пользовательского JwtAuthenticationPrivider я заметил, что исключения, связанные с JWT, которые могут возникнуть при синтаксическом анализе токена, обрабатываются неправильно.

401 отправляется обратно клиенту, но исключение отображается как ошибка в журналах, что не является правильным в моих глазах. Это явно ошибка клиента, и ее не следует указывать как ошибку в журнале сервера.

2020-04-02 13:43:26.616 ERROR 1666943 --- [nio-8080-exec-2] o.a.c.c.C.[.[.[/].[dispatcherServlet]    : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception

io.jsonwebtoken.security.SignatureException: JWT signature does not match locally computed signature. JWT validity cannot be asserted and should not be trusted.

Поскольку я уже использую ResponseEntityExceptionHandler для исключений обработчика, было бы хорошо используйте его и для цепочки фильтров. Я знаю, что аннотация @ ExceptionHandler работает только с обработчиками.

Но поскольку они создают только объект ResponseEntity , я мог бы использовать эти методы для создания сообщений об ошибках для цепочка фильтров.

Мне трудно найти пружинный идиоматический c способ.

Моя первая идея заключается в следующем:

public class JwtAuthorizationFilter extends OncePerRequestFilter {

    ... 

 @Override
    protected void doFilterInternal(HttpServletRequest req, HttpServletResponse res, FilterChain chain)
            throws IOException, ServletException {

        try {
            String header = req.getHeader(AUTHORIZATION_HEADER);
            if (header != null && header.startsWith(TOKEN_PREFIX)) {
                var token = header.replace(TOKEN_PREFIX, "");
                var jwtAuthenticationAttempt = new JwtAuthentication(token);
                var jwtAuthenticationResult = this.authenticationManager.authenticate(jwtAuthenticationAttempt);
                SecurityContextHolder.getContext().setAuthentication(jwtAuthenticationResult);
            }
        } catch (Exception ex) {
            // Build Error Response by a global ResponseEntityExceptionHandler
            // Break Filter chain
            // Send error back to client
        }
        chain.doFilter(req, res);
    }
}

Он просто оборачивает попробуйте перехватить критические операции.

Моя вторая идея:

@Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
            throws ServletException, IOException {

        try {
            filterChain.doFilter(request, response);
        } catch (Exception ex) {
            // Build Error Response by a global ResponseEntityExceptionHandler
            // Break Filter chain
            // Send error back to client
        }
    }

Здесь я регистрирую фильтр как можно раньше в цепочке фильтров и объединяю блок try catch вокруг операции фильтра. .

У обеих возможностей есть недостаток, заключающийся в том, что мне приходится проводить различие в двух случаях. Сначала в готовом handleException методе ResponseEntityExceptionHandler и во втором разе в моих решениях, показанных выше, которые вызывают специфический c метод обработчика ResponseEntityExceptionHandler для создания объекта ResponseEntity .

public abstract class ResponseEntityExceptionHandler {

    ... 

@Nullable
    public final ResponseEntity<Object> handleException(Exception ex, WebRequest request) throws Exception {
        HttpHeaders headers = new HttpHeaders();

        if (ex instanceof HttpRequestMethodNotSupportedException) {
            HttpStatus status = HttpStatus.METHOD_NOT_ALLOWED;
            return handleHttpRequestMethodNotSupported((HttpRequestMethodNotSupportedException) ex, headers, status, request);
        }
        else if (ex instanceof HttpMediaTypeNotSupportedException) {
            HttpStatus status = HttpStatus.UNSUPPORTED_MEDIA_TYPE;
            return handleHttpMediaTypeNotSupported((HttpMediaTypeNotSupportedException) ex, headers, status, request);
        }
        ...
        else {
            // Unknown exception, typically a wrapper with a common MVC exception as cause
            // (since @ExceptionHandler type declarations also match first-level causes):
            // We only deal with top-level MVC exceptions here, so let's rethrow the given
            // exception for further processing through the HandlerExceptionResolver chain.
            throw ex;
        }
    }
}

Было бы идеально, если бы я мог вызвать метод handleException непосредственно из фильтра. Что мешает мне, так это то, что этот метод выдает пропущенное исключение, если обработчик не найден и поскольку метод объявлен как финальный, я не могу реализовать пользовательское решение.

Есть ли подходящее решение для глобальной ошибки обработчик?

Заранее спасибо

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...