【问题标题】:WebTestClient - CORS with Spring Boot and WebfluxWebTestClient - 带有 Spring Boot 和 Webflux 的 CORS
【发布时间】:2020-10-24 15:20:50
【问题描述】:

我有 Vuejs 前端和一个 Spring Boot Webflux 控制器。现在浏览器在调用 Spring Boot 时会抱怨 CORS。

Access to XMLHttpRequest at 'https://...' from origin 'https://...' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.

为了启用 CORS,我尝试使用@CrossOrigin 注释,如下所述:https://www.baeldung.com/spring-webflux-cors

但这并没有帮助,并且即使在控制器中使用 @CrossOrigin 注释,Spring 也不会在响应中将 CORS 标头发送回。

我还尝试了 Bealdung 教程中描述的 WebTestClient,它确认标题不存在:

java.lang.AssertionError: Response header 'Access-Control-Allow-Origin' expected:<[*]> but was:<null>

> OPTIONS /cors-enabled-endpoint
> WebTestClient-Request-Id: [1]
> Origin: [http://any-origin.com]
> Access-Control-Request-Method: [GET]

No content

< 500 INTERNAL_SERVER_ERROR Internal Server Error
< Vary: [Origin, Access-Control-Request-Method, Access-Control-Request-Headers]

0 bytes of content (unknown content-type).

如果我用response.expectHeader().exists("Access-Control-Allow-Origin"); 测试 我明白了:

java.lang.AssertionError: Response header 'Access-Control-Allow-Origin' does not exist

知道为什么上面链接中描述的 CORS 配置不起作用吗?我还尝试在全局配置上启用 CORS 并使用 WebFilter 启用 CORS。但似乎没有任何效果。

【问题讨论】:

    标签: java spring-boot cors spring-webflux same-origin-policy


    【解决方案1】:

    @CrossOriginJavaDocs 中有提及

    Spring Web MVC 和 Spring WebFlux 都通过 RequestMappingHandlerMapping 在各自的模块中。这 添加来自每个类型和方法级别注释对的值 到 CorsConfiguration

    这意味着您的 CORS 设置将添加到 CorsConfiguration

    如果您使用 Spring Security,您需要 enable CORS 以便使用您的 CorsConfiguration。看下面一个小例子

       protected void configure(HttpSecurity http) throws Exception {
            http
                .cors()
                ...
        }
    

    来自JavaDocs

    添加要使用的 CorsFilter。如果名为 corsFilter 的 bean 是 提供,使用 CorsFilter。否则,如果 corsConfigurationSource 是 定义,然后使用 CorsConfiguration。否则,如果 Spring MVC 在使用 HandlerMappingIntrospector 的类路径上。

    注意,如果您通过请求 (cookie) 发送凭据,则需要将其添加到您的 CORS 设置中

    @CrossOrigin(allowCredentials = "true")
    

    【讨论】:

    • 非常感谢您的回答。仔细阅读并再次阅读文档后,我仍然遇到同样的问题。实际上,我不使用 Spring Security。还有一件事:运行测试也给出了这个错误:2020-07-04 13:44:45.611 ERROR 17867 --- [ parallel-1] o.s.w.s.adapter.HttpWebHandlerAdapter : [38a66881] 500 Server Error for HTTP GET "/cors-enabled-endpoint"java.lang.IllegalArgumentException: Actual request scheme must not be null这可能是根本原因,但我在互联网上找不到帮助资源。你能帮忙吗?
    • @RichArt 确保将 CrossOrigin 放在与您发送请求的完全相同的请求映射上。如果您将 CrossOrigin 放在 GET "/api/users" 上,那么 CORS 将仅对 GET "/api/users" 启用。您也可以将 CrossOrigin 放在控制器级别,然后 CORS 将可用于控制器中的所有请求映射,例如POST "/api/users" GET "/api/users" 等等,当然,这取决于你在控制器的 CORS 配置中允许的方法。
    【解决方案2】:

    解决方案是为 WebTestClient 使用以下配置:

    @SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
    @AutoConfigureWebTestClient
    
    class MyTestClass {
    
        @Autowired
        private WebTestClient webTestClient;
    
        @Test
        void corsTest() {
            ResponseSpec response = webTestClient.get()
                    .uri("/cors-enabled-endpoint")
                    .header("Origin", "http://any-origin.com")
                    .exchange();
    
            response.expectHeader()
                    .valueEquals("Access-Control-Allow-Origin", "*");
        }
    
    }
    

    而且我还需要添加spring-boot-starter-web 依赖,这很奇怪,因为我只使用Webflux。但是没有它,测试仍然会失败,IllegalArgumentException: Actual request scheme must not be null

    <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    

    现在,测试是绿色的!

    【讨论】:

      【解决方案3】:

      对我来说,不添加 spring-starter-web 依赖项的实际工作是使用 web 过滤器并在 http 方法为 OPTIONS 时强制返回。

      @Configuration
      public class CorsGlobalConfiguration implements WebFilter {
      
      
      @Override
      public Mono<Void> filter(ServerWebExchange serverWebExchange,
                               WebFilterChain webFilterChain) {
          ServerHttpRequest request = serverWebExchange.getRequest();
          ServerHttpResponse response = serverWebExchange.getResponse();
          HttpHeaders headers = response.getHeaders();
          headers.add(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN, "*");
          headers.add(HttpHeaders.ACCESS_CONTROL_ALLOW_METHODS, "POST, GET, PUT, OPTIONS, DELETE, PATCH");
          headers.add(HttpHeaders.ACCESS_CONTROL_ALLOW_CREDENTIALS, "true");
          headers.add(HttpHeaders.ACCESS_CONTROL_ALLOW_HEADERS, "*");
          headers.add(HttpHeaders.ACCESS_CONTROL_EXPOSE_HEADERS, "*");
          headers.add(HttpHeaders.ACCESS_CONTROL_MAX_AGE, "18000L");
          if (request.getMethod() == HttpMethod.OPTIONS) {
              response.setStatusCode(HttpStatus.OK);
              return Mono.empty();//HERE
          }
          return webFilterChain.filter(serverWebExchange);
      }
      
      
      }
      

      Source

      【讨论】:

        【解决方案4】:

        @RichArt 的解决方案 99% 接近 - 但您不需要依赖项 (spring-boot-starter-web);由于它正在检查 CORS,因此 Spring 似乎需要帮助了解您在测试中使用的方案和主机;它无法派生“方案”(http)或具有较短 URI 的主机。

        @SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
        @AutoConfigureWebTestClient
        class MyTestClass {
        
            @Autowired
            private WebTestClient webTestClient;
        
            @LocalServerPort
            private Int lsp;
        
            @Test
            void corsTest() {
                ResponseSpec response = webTestClient.get()
                        .uri("http://localhost" + lsp.toString() + "/cors-enabled-endpoint")
                        .header("Origin", "http://any-origin.com")
                        .exchange();
        
                response.expectHeader()
                        .valueEquals("Access-Control-Allow-Origin", "*");
            }
        
        }
        

        【讨论】:

        • 非常感谢。是的,添加 mvc 的东西可能不是一个好主意。
        【解决方案5】:

        Thinice 的解决方案是 99.9%。 Spring WebTestClient CORS 测试要求 URI 包含 any 主机名和端口。 实际主机名和端口被 WebTestClient 忽略以连接到测试 Web 服务器:

        @SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
        @AutoConfigureWebTestClient
        class MyTestClass {
        
            @Autowired
            private WebTestClient webTestClient;
            
            @Test
            void corsTest() {
                webTestClient.get()
                        .uri("http://hostname-ignored:666/cors-enabled-endpoint")
                        .header("Origin", "http://any-origin.com")
                        .exchange()   
                        .expectHeader()
                          .valueEquals("Access-Control-Allow-Origin", "*");
            }    
        }
        

        此外,可以省略显式端口,它将继续工作:

        @SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
        @AutoConfigureWebTestClient
        class MyTestClass {
        
            @Autowired
            private WebTestClient webTestClient;
            
            @Test
            void corsTest() {
                webTestClient.get()
                        .uri("http://look-ma-no-port/cors-enabled-endpoint")
                        .header("Origin", "http://any-origin.com")
                        .exchange()   
                        .expectHeader()
                          .valueEquals("Access-Control-Allow-Origin", "*");
            }
        }
        

        要为所有端点启用 CORS,可以使用给定的 WebFluxConfigurer:

        @Configuration(proxyBeanMethods = false)
        public class MyWebFluxConfigurer {
        
          @Bean
          public WebFluxConfigurer corsConfigurer() {
            return new WebFluxConfigurerComposite() {
        
              @Override
              public void addCorsMappings(CorsRegistry registry) {
                registry.addMapping("/**").allowedOrigins("*").allowedMethods("*");
              }
            };
          }
        }
        
        • 包含 Origin 标头很重要。如果没有 Origin 标头,则不会设置 Access-Control-Allow-Origin 标头,并且测试将失败。

        • 将 Origin 设置为与 URI 相同的主机名将被视为同源,因此不会设置跨域标头。

        • RichArt 建议包含 spring-boot-starter-web 依赖项并不是启用 CORS 测试的具体要求。

        • 此答案仅适用于测试。它假定您已在服务器中启用 CORS。

        • 我的答案是使用 Spring WebFlux 5.3.10。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2021-08-17
          • 1970-01-01
          • 2018-01-08
          • 2019-05-05
          • 2019-07-21
          • 1970-01-01
          • 2021-07-31
          相关资源
          最近更新 更多