Выбрать главу

Это примерно эквивалентно следующему примеру, который использует retrieve():

Mono<Ingredient> ingredientMono = webClient

   .get()

   .uri("http://localhost:8080/ingredients/{id}", ingredientId)

   .retrieve()

   .bodyToMono(Ingredient.class);

В примере exchange() вместо использования ResponseSpec объекта bodyToMono() для получения Mono<Ingredient> вы получаете Mono<ClientResponse>, к которому можно применить функцию плоского отображения для сопоставления ClientResponse с Mono<Ingredient>, который сглаживается в результирующий Mono.

Теперь давайте посмотрим, что отличает exchange(). Предположим, что ответ на запрос может включать заголовок с именем X_UNAVAILABLE со значением true, указывающим на то, что (по какой-то причине) рассматриваемый ингредиент недоступен. И для обсуждения, предположим, что если этот заголовок существует, вы хотите, чтобы результирующий Mono был пустым — ничего не возвращать. Вы можете достичь этого сценария, добавив еще один вызов flatMap() таким образом, чтобы весь вызов веб-клиента выглядел следующим образом:

Mono<Ingredient> ingredientMono = webClient

   .get()

   .uri("http://localhost:8080/ingredients/{id}", ingredientId)

   .exchange()

   .flatMap(cr -> {

      if (cr.headers().header("X_UNAVAILABLE").contains("true")) {

         return Mono.empty();

      }

      return Mono.just(cr);

   })

   .flatMap(cr -> cr.bodyToMono(Ingredient.class));

Новый вызов flatMap() проверяет заголовки данного объекта запроса клиента, ища заголовок с именем X_UNAVAILABLE со значением true. Если он найден, он возвращает пустое Mono. В противном случае он возвращает новое Mono, содержащее ответ клиента. В любом случае, возвращенный Mono будет сглажен в Mono, с которым будет работать следующий вызов flatMap().

11.5 Securing reactive web APIs

Пока существует Spring Security (и даже до этого, когда он был известен как Acegi Security), его модель веб-безопасности была построена вокруг фильтров сервлетов. Ну, это имеет смысл. Если вам нужно перехватить запрос, привязанный к веб framework-у на основе сервлета, чтобы гарантировать, что у инициатора запроса есть надлежащие полномочия, фильтр сервлета является очевидным выбором. Но But Spring WebFlux вносит изменения в этот подход.

При написании веб-приложения с использованием Spring WebFlux нет никакой гарантии, что сервлеты будут задействованы. Фактически, реактивное веб-приложение, скорее всего, будет построено на Netty или каком-либо другом сервере, не являющемся сервлетом. Означает ли это, что Spring Security на основе фильтра сервлетов нельзя использовать для защиты приложений Spring WebFlux?

Это правда, что с помощью сервлетов, фильтров не подойдет при защите приложения Spring WebFlux. Но Spring Security все еще справляется с этой задачей. Начиная с версии 5.0.0, Spring Security можно использовать для защиты Spring MVC на основе сервлетов и реактивных приложений Spring WebFlux. Для этого используется Spring WebFilter, Spring-специфичный фильтр сервлетов, который не требует зависимости от API сервлета.

Что еще более примечательно, так это то, что модель конфигурации для реактивного Spring Security не сильно отличается от того, что вы видели в главе 4. Фактически, в отличие от Spring WebFlux, который имеет отдельную зависимость от Spring MVC, Spring Security является тот же стартер безопасности Spring Boot, независимо от того, собираетесь ли вы использовать его для защиты веб-приложения Spring MVC или приложения, написанного с использованием Spring WebFlux. Как напоминание, вот как выглядит security стартер:

<dependency>

   <groupId>org.springframework.boot</groupId>

   <artifactId>spring-boot-starter-security</artifactId>

</dependency>

Тем не менее, есть несколько небольших различий между моделями реактивной и нереактивной конфигурации Spring Security. Стоит взглянуть на сравнение двух моделей конфигурации.

11.5.1 Настройка реактивного web security

Напомним, что настройка Spring Security для защиты веб-приложения Spring MVC обычно включает в себя создание нового класса конфигурации, расширяющего WebSecurityConfigurerAdapter и снабженного аннотацией @EnableWebSecurity. Такой класс конфигурации переопределяет метод configuration() для указания специфики web security, например, какие полномочия требуются для определенных путей запроса. Следующий простой класс конфигурации Spring Security служит напоминанием о том, как настроить безопасность для нереактивного приложения Spring MVC:

@Configuration

@EnableWebSecurity

public class SecurityConfig extends WebSecurityConfigurerAdapter {

   @Override

   protected void configure(HttpSecurity http) throws Exception {

      http

         .authorizeRequests()

         .antMatchers("/design", "/orders").hasAuthority("USER")

         .antMatchers("/**").permitAll();

   }

}

Теперь давайте посмотрим, как эта же конфигурация может выглядеть для реактивного приложения Spring WebFlux. В следующем списке показан класс конфигурации реактивной безопасности, который примерно эквивалентен простой конфигурации безопасности из предыдущих версий.

Листинг 11.2. Настройка Spring Security для приложения Spring WebFlux