Обработчик @ControllerAdvice не вызывается из пользовательского фильтра Spring Cloud

Я создал следующий пользовательский фильтр, который будет использоваться для авторизации в приложении Spring Cloud. Вот метод apply() из этого фильтра, из которого выбрасывается исключение в случае неудачной проверки авторизации:

@Override
public GatewayFilter apply(Config config) {
    return (exchange, chain) -> {
        HttpStatus status = HttpStatus.FORBIDDEN;

        try {
            String authRequest = exchange.getRequest().getHeaders().getFirst(
                 Constants.SESSION_HEADER_NAME);
            HttpHeaders headers = new HttpHeaders();
            HttpEntity<String> entity = new HttpEntity<String>(authRequest, headers);
            // make REST call to authorization module
            status = restTemplate.postForEntity(authURL, entity, String.class).getStatusCode();
        }
        catch (Exception e) {
            LOGGER.error("Something went wrong during authorization", e);
        }

        // throw an exception if anything went
        // wrong with authorization
        if (!HttpStatus.OK.equals(status)) {
            throw new ResponseStatusException(HttpStatus.FORBIDDEN);
        }

        return chain.filter(exchange);
    };
}

Я определил следующий класс @ControllerAdvice для перехвата всех исключений, создаваемых указанным выше фильтром Gateway Cloud:

@ControllerAdvice
public class RestExceptionHandler {
    @ExceptionHandler(value = ResponseStatusException.class)
    public final ResponseEntity<Object> handleException(ResponseStatusException ex) {
        return new ResponseEntity<>("UNAUTHORIZED", HttpStatus.FORBIDDEN);
    }
}

В настоящее время я наблюдаю следующее:

  • Пользовательский фильтр ResponseStatusException выше — это нет, который перехватывается методом сопоставления @ControllerAdvice.
  • Тем не менее, выбрасывая это исключение из другого места в приложении Spring Boot, например. обычный контроллер, который мы используем в качестве конечной точки аутентификации, является, пойманный методом @ControllerAdvice.

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

Кто-нибудь может пролить свет на эту проблему?

Можете ли вы также показать механизм, как этот фильтр подключается к цепочке обработки сервлетов?

luboskrnac 01.04.2019 10:29

@luboskrnac Я этого не знаю, поэтому я задал вопрос :-) ... доступная документация для шлюза Spring Cloud довольно скудна, оставляя единственные реальные варианты, такие как переполнение стека или самостоятельное чтение исходного кода.

Tim Biegeleisen 01.04.2019 10:31

Spring должен знать об этом фильтре, чтобы применить его. Можете ли вы показать полный класс? Обычно фильтры сервлетов подключаются к вашей конфигурации Spring Security. Если этот фильтр применяется, в вашем приложении уже есть какой-то механизм.

luboskrnac 01.04.2019 10:34

Это не веб-фильтр J2EE, и хотя он может подключаться к Spring Security под капотом, такие детали реализации скрыты от тех, кто использует Spring Cloud.

Tim Biegeleisen 01.04.2019 10:36

Spring Cloud — это просто набор помощников для интеграции с распространенными облачными шаблонами/сервисами. Spring Boot (с помощью Spring MVC и Spring Security + контейнер сервлетов) обрабатывает запросы. Так что по сути это стандартный фильтр сервлетов из мира J2EE.

luboskrnac 01.04.2019 10:39

@luboskrnac Хорошо, продолжая ваш комментарий, есть ли какое-либо общее средство, позволяющее заставить @ControllerAdvice работать с исключениями, поступающими из фильтров J2EE?

Tim Biegeleisen 01.04.2019 10:52
@ControllerAdvice не работает с фильтрами Spring Cloud Gateway.
spencergibb 01.04.2019 17:28

@spencergibb Привет, Спенсер, спасибо, что прочитали мой вопрос. Я в основном должен прийти к этому выводу уже. Не могли бы вы опубликовать ответ о наилучшей практике создания исключения из облачного фильтра?

Tim Biegeleisen 01.04.2019 17:29
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Версия Java на основе версии загрузки
Версия Java на основе версии загрузки
Если вы зайдете на официальный сайт Spring Boot , там представлен start.spring.io , который упрощает создание проектов Spring Boot, как показано ниже.
Документирование API с помощью Swagger на Springboot
Документирование API с помощью Swagger на Springboot
В предыдущей статье мы уже узнали, как создать Rest API с помощью Springboot и MySql .
1
8
1 082
2

Ответы 2

Из Javadocs ControllerAdvice:

Classes with @ControllerAdvice can be declared explicitly as Spring beans or auto-detected via classpath scanning.

Вы не показали полный класс для своего фильтра, но могу поспорить, что это не Spring Bean, просканированный в пути к классам. Обычно фильтры сервлетов явно подключаются к конфигурации Spring Security. Поэтому обработка ControllerAdvice игнорирует его.

Облачный фильтр находится в каталоге filter, который находится непосредственно под классом, содержащим контроллер @SpringBootApplication.

Tim Biegeleisen 01.04.2019 10:28

Я предполагаю, что под фильтром вы имеете в виду javax.servlet.Filter.

В этом случае @ControllerAdvice не работает. Он используется для обработки исключений от контроллеров. Но вы выбрасываете исключение еще до того, как оно сможет распространиться на контроллер (не вызывая метод chain.filter(exchange).

Попробуйте создавать исключения в контроллере, а не в фильтре.

Редактировать: Если вы не хотите обрабатывать исключения в @Controller, вы должны реализовать обработчик терминала в javax.servlet.Filter напрямую. Это означает, что нужно изменить ответ на входящий запрос напрямую следующим образом:

HttpServletResponse httpResponse = (HttpServletResponse) exchange.getResponse();

// either format the response by yourself
httpResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
httpResponse.setHeader(...)
...

// or let populate error directly
httpResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED);

... это то, что @ControllerAdvice делает внутри.

Интересное предложение. Итак, в этом случае будет ли правильным шаблоном просто генерировать исключения терминала непосредственно из пользовательского облачного фильтра?

Tim Biegeleisen 01.04.2019 10:29

Отредактировано, чтобы ответить на ваш дополнительный вопрос.

Michal Stepan 01.04.2019 13:47

Другие вопросы по теме