WebTestClient - 带有 Spring Boot 和 Webflux 的 CORS
Posted
技术标签:
【中文标题】WebTestClient - 带有 Spring Boot 和 Webflux 的 CORS【英文标题】:WebTestClient - CORS with Spring Boot and Webflux 【发布时间】: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。但似乎没有任何效果。
【问题讨论】:
【参考方案1】:在@CrossOrigin
的JavaDocs 中有提及
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。
【讨论】:
以上是关于WebTestClient - 带有 Spring Boot 和 Webflux 的 CORS的主要内容,如果未能解决你的问题,请参考以下文章
spring boot:如何配置自动装配的 WebTestClient
Spring boot - WebFlux - WebTestClient - 将响应转换为 responseEntity
SpringBoot2 + Webflux - WebTestClient 总是返回“401 UNAUTHORIZED”