Spring Webflux e CORS

Spring Webflux e CORS

1. Visão geral

Em aprevious post, aprendemos sobre a especificação Cross-Origin Resource Sharing (CORS) e como usá-la no Spring.

Neste tutorial rápido,we’ll set up a similar CORS configuration using Spring’s 5 WebFlux framework.

Em primeiro lugar, veremos como podemos ativar o mecanismo em APIs baseadas em anotação.

Em seguida, analisaremos como habilitá-lo em todo o projeto como uma configuração global ou usando umWebFilter especial.

2. Habilitando o CORS em elementos anotados

Spring fornece a anotação@CrossOrigin para habilitar solicitações CORS em classes de controlador e / ou métodos de tratamento.

2.1. Usando@CrossOrigin em um método de manipulador de solicitação

Vamos adicionar esta anotação ao nosso método de solicitação mapeado:

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

Usaremos umWebTestClient (como explicamos na seção '4. Teste 'dethis post) para analisar a resposta que obtemos deste endpoint:

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

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

Além disso, podemos experimentar uma solicitação de comprovação para garantir que a configuração do CORS esteja funcionando conforme o esperado:

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");

A anotação@CrossOrigin tem a seguinte configuração padrão:

  • Permite todas as origens (que explica o valor ‘* 'no cabeçalho da resposta)

  • Permite todos os cabeçalhos

  • Todos os métodos HTTP mapeados pelo método manipulador são permitidos

  • Credenciais não estão ativadas

  • O valor de "idade máxima" é de 1800 segundos (30 minutos)

No entanto, qualquer um desses valores pode ser substituído usando os parâmetros da anotação.

2.2. Usando@CrossOrigin no controlador

Essa anotação também é suportada no nível de classe e afetará todos os seus métodos.

Caso a configuração de nível de classe não seja adequada para todos os nossos métodos, podemos anotar ambos os elementos para obter o resultado desejado:

@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. Habilitando o CORS na configuração global

Também podemos definir uma configuração CORS global substituindo o métodoaddCorsMappings() de uma implementaçãoWebFluxConfigurer.

Além disso, a implementação precisa da anotação@EnableWebFlux para importar a configuração 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);
    }
}

Como resultado, estamos habilitando o tratamento de solicitações de origem cruzada para esse padrão de caminho específico.

A configuração padrão é semelhante à de@CrossOrigin, mas apenas com os métodosGET,HEAD ePOST permitidos.

Também podemos combinar essa configuração com uma configuração local:

  • Para os atributos de valores múltiplos, a configuração CORS resultante será a adição de cada especificação

  • Por outro lado, os valores locais terão precedência sobre os globais para os de valor único

Porém, o uso dessa abordagem não é eficaz para terminais funcionais.

4. Habilitando CORS com umWebFilter

A melhor maneira de habilitar o CORS em terminais funcionais é usando umWebFilter.

Como vimosin this post, podemos usarWebFilters para modificar solicitações e respostas, mantendo a implementação do endpoint intacta.

O Spring forneceCorsWebFilter embutido para lidar facilmente com as configurações de origem cruzada:

@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);
}

Isso também é eficaz para manipuladores anotados, mas não pode ser combinado com uma configuração@CrossOrigin mais refinada.

Devemos ter em mente queCorsConfiguration não possui uma configuração padrão.

Portanto, a menos que especifiquemos todos os atributos relevantes, a implementação do CORS será bastante restritiva.

Uma maneira simples de definir os valores padrão é usando o métodoapplyPermitDefaultValues() no objeto.

5. Conclusão

Em conclusão, aprendemos com exemplos muito curtos de como habilitar o CORS em nosso serviço baseado em webflux.

Vimos abordagens diferentes, portanto, tudo o que precisamos fazer agora é analisar qual deles se adapta melhor aos nossos requisitos.

Podemos encontrar muitos exemplos emour Github repo, junto com casos de teste onde analisamos a maioria dos casos extremos relacionados a este tópico.