Spring Webflux и CORS

Spring Webflux и CORS

1. обзор

Вprevious post мы узнали о спецификации Cross-Origin Resource Sharing (CORS) и о том, как использовать ее в Spring.

В этом кратком руководствеwe’ll set up a similar CORS configuration using Spring’s 5 WebFlux framework.

Прежде всего, мы увидим, как мы можем включить этот механизм в API на основе аннотаций.

Затем мы проанализируем, как включить его для всего проекта в виде глобальной конфигурации или с помощью специальногоWebFilter.

2. Включение CORS для аннотированных элементов

Spring предоставляет аннотацию@CrossOrigin для включения запросов CORS для классов контроллеров и / или методов обработчиков.

2.1. Использование@CrossOrigin в методе обработчика запросов

Давайте добавим эту аннотацию к нашему методу сопоставленного запроса:

@CrossOrigin
@PutMapping("/cors-enabled-endpoint")
public Mono corsEnabledEndpoint() {
    // ...
}

Мы будем использоватьWebTestClient (как мы объясняли в разделе ‘4. Тестированиеthis post) для анализа ответа, который мы получаем от этой конечной точки:

ResponseSpec response = webTestClient.put()
  .uri("/cors-enabled-endpoint")
  .header("Origin", "http://any-origin.com")
  .exchange();

response.expectHeader()
  .valueEquals("Access-Control-Allow-Origin", "*");

Кроме того, мы можем попробовать предварительный запрос, чтобы убедиться, что конфигурация CORS работает должным образом:

ResponseSpec response = webTestClient.options()
  .uri("/cors-enabled-endpoint")
  .header("Origin", "http://any-origin.com")
  .header("Access-Control-Request-Method", "PUT")
  .exchange();

response.expectHeader()
  .valueEquals("Access-Control-Allow-Origin", "*");
response.expectHeader()
  .valueEquals("Access-Control-Allow-Methods", "PUT");
response.expectHeader()
  .exists("Access-Control-Max-Age");

Аннотация@CrossOrigin имеет следующую конфигурацию по умолчанию:

  • Разрешает все источники (что объясняет значение ‘* 'в заголовке ответа)

  • Позволяет все заголовки

  • Все методы HTTP, сопоставленные методом обработчика, разрешены

  • Учетные данные не включены

  • Значение max-age составляет 1800 секунд (30 минут)

Однако любое из этих значений можно переопределить с помощью параметров аннотации.

2.2. Использование@CrossOrigin на контроллере

Эта аннотация также поддерживается на уровне класса и будет влиять на все его методы.

Если конфигурация на уровне класса подходит не для всех наших методов, мы можем аннотировать оба элемента, чтобы получить желаемый результат:

@CrossOrigin(value = { "http://allowed-origin.com" },
  allowedHeaders = { "example-Allowed" },
  maxAge = 900
)
@RestController
public class CorsOnClassController {

    @PutMapping("/cors-enabled-endpoint")
    public Mono corsEnabledEndpoint() {
        // ...
    }

    @CrossOrigin({ "http://another-allowed-origin.com" })
    @PutMapping("/endpoint-with-extra-origin-allowed")
    public Mono corsEnabledWithExtraAllowedOrigin() {
        // ...
    }

    // ...
}

3. Включение CORS в глобальной конфигурации

Мы также можем определить глобальную конфигурацию CORS, переопределив методaddCorsMappings() реализацииWebFluxConfigurer.

Кроме того, реализации требуется аннотация@EnableWebFlux для импорта конфигурации Spring WebFlux:

@Configuration
@EnableWebFlux
public class CorsGlobalConfiguration implements WebFluxConfigurer {

    @Override
    public void addCorsMappings(CorsRegistry corsRegistry) {
        corsRegistry.addMapping("/**")
          .allowedOrigins("http://allowed-origin.com")
          .allowedMethods("PUT")
          .maxAge(3600);
    }
}

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

Конфигурация по умолчанию аналогична конфигурации@CrossOrigin, но разрешены только методыGET,HEAD иPOST.

Мы также можем объединить эту конфигурацию с локальной:

  • Для атрибутов с несколькими значениями результирующая конфигурация CORS будет дополнением каждой спецификации

  • С другой стороны, локальные значения будут иметь приоритет над глобальными для однозначных

Однако использование этого подхода неэффективно для функциональных конечных точек.

4. Включение CORS с помощьюWebFilter

Лучший способ включить CORS на функциональных конечных точках - использоватьWebFilter.

Как мы виделиin this post, мы можем использоватьWebFilters для изменения запросов и ответов, сохраняя при этом реализацию конечной точки.

Spring предоставляет встроенныйCorsWebFilter, чтобы легко справляться с конфигурациями перекрестного происхождения:

@Bean
CorsWebFilter corsWebFilter() {
    CorsConfiguration corsConfig = new CorsConfiguration();
    corsConfig.setAllowedOrigins(Arrays.asList("http://allowed-origin.com"));
    corsConfig.setMaxAge(8000L);
    corsConfig.addAllowedMethod("PUT");
    corsConfig.addAllowedHeader("example-Allowed");

    UrlBasedCorsConfigurationSource source =
      new UrlBasedCorsConfigurationSource();
    source.registerCorsConfiguration("/**", corsConfig);

    return new CorsWebFilter(source);
}

Это также эффективно для обработчиков с аннотациями, но его нельзя комбинировать с более тонкой конфигурацией@CrossOrigin.

Мы должны помнить, чтоCorsConfiguration не имеет конфигурации по умолчанию.

Таким образом, если мы не укажем все соответствующие атрибуты, реализация CORS будет в значительной степени ограничительной.

Простой способ установить значения по умолчанию - использовать методapplyPermitDefaultValues() для объекта.

5. Заключение

В заключение мы узнали на очень коротких примерах, как включить CORS в нашем сервисе на основе webflux.

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

Мы можем найти множество примеров вour Github repo вместе с тестовыми примерами, в которых мы анализируем большинство крайних случаев по этой теме.