Improve CORS handling
This commit improves CORS support by: - Using CORS processing only for CORS-enabled endpoints - Skipping CORS processing for same-origin requests - Adding Vary headers for non-CORS requests It introduces an AbstractHandlerMapping#hasCorsConfigurationSource method in order to be able to check CORS endpoints efficiently. Closes gh-22273 Closes gh-22496
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2015 the original author or authors.
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -20,6 +20,10 @@ import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.web.util.UriComponents;
|
||||
import org.springframework.web.util.UriComponentsBuilder;
|
||||
|
||||
/**
|
||||
* Utility class for CORS request handling based on the
|
||||
@@ -31,17 +35,43 @@ import org.springframework.http.HttpMethod;
|
||||
public abstract class CorsUtils {
|
||||
|
||||
/**
|
||||
* Returns {@code true} if the request is a valid CORS one.
|
||||
* Returns {@code true} if the request is a valid CORS one by checking {@code Origin}
|
||||
* header presence and ensuring that origins are different.
|
||||
*/
|
||||
public static boolean isCorsRequest(HttpServletRequest request) {
|
||||
return (request.getHeader(HttpHeaders.ORIGIN) != null);
|
||||
String origin = request.getHeader(HttpHeaders.ORIGIN);
|
||||
if (origin == null) {
|
||||
return false;
|
||||
}
|
||||
UriComponents originUrl = UriComponentsBuilder.fromOriginHeader(origin).build();
|
||||
String scheme = request.getScheme();
|
||||
String host = request.getServerName();
|
||||
int port = request.getServerPort();
|
||||
return !(ObjectUtils.nullSafeEquals(scheme, originUrl.getScheme()) &&
|
||||
ObjectUtils.nullSafeEquals(host, originUrl.getHost()) &&
|
||||
getPort(scheme, port) == getPort(originUrl.getScheme(), originUrl.getPort()));
|
||||
|
||||
}
|
||||
|
||||
private static int getPort(@Nullable String scheme, int port) {
|
||||
if (port == -1) {
|
||||
if ("http".equals(scheme) || "ws".equals(scheme)) {
|
||||
port = 80;
|
||||
}
|
||||
else if ("https".equals(scheme) || "wss".equals(scheme)) {
|
||||
port = 443;
|
||||
}
|
||||
}
|
||||
return port;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@code true} if the request is a valid CORS pre-flight one.
|
||||
* To be used in combination with {@link #isCorsRequest(HttpServletRequest)} since
|
||||
* regular CORS checks are not invoked here for performance reasons.
|
||||
*/
|
||||
public static boolean isPreFlightRequest(HttpServletRequest request) {
|
||||
return (isCorsRequest(request) && HttpMethod.OPTIONS.matches(request.getMethod()) &&
|
||||
return (HttpMethod.OPTIONS.matches(request.getMethod()) &&
|
||||
request.getHeader(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD) != null);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -19,7 +19,6 @@ package org.springframework.web.cors;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
@@ -36,7 +35,6 @@ import org.springframework.http.server.ServletServerHttpRequest;
|
||||
import org.springframework.http.server.ServletServerHttpResponse;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.web.util.WebUtils;
|
||||
|
||||
/**
|
||||
* The default implementation of {@link CorsProcessor}, as defined by the
|
||||
@@ -45,8 +43,7 @@ import org.springframework.web.util.WebUtils;
|
||||
* <p>Note that when input {@link CorsConfiguration} is {@code null}, this
|
||||
* implementation does not reject simple or actual requests outright but simply
|
||||
* avoid adding CORS headers to the response. CORS processing is also skipped
|
||||
* if the response already contains CORS headers, or if the request is detected
|
||||
* as a same-origin one.
|
||||
* if the response already contains CORS headers.
|
||||
*
|
||||
* @author Sebastien Deleuze
|
||||
* @author Rossen Stoyanchev
|
||||
@@ -62,26 +59,23 @@ public class DefaultCorsProcessor implements CorsProcessor {
|
||||
public boolean processRequest(@Nullable CorsConfiguration config, HttpServletRequest request,
|
||||
HttpServletResponse response) throws IOException {
|
||||
|
||||
response.addHeader(HttpHeaders.VARY, HttpHeaders.ORIGIN);
|
||||
response.addHeader(HttpHeaders.VARY, HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD);
|
||||
response.addHeader(HttpHeaders.VARY, HttpHeaders.ACCESS_CONTROL_REQUEST_HEADERS);
|
||||
|
||||
if (!CorsUtils.isCorsRequest(request)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
ServletServerHttpResponse serverResponse = new ServletServerHttpResponse(response);
|
||||
if (responseHasCors(serverResponse)) {
|
||||
if (response.getHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN) != null) {
|
||||
logger.trace("Skip: response already contains \"Access-Control-Allow-Origin\"");
|
||||
return true;
|
||||
}
|
||||
|
||||
ServletServerHttpRequest serverRequest = new ServletServerHttpRequest(request);
|
||||
if (WebUtils.isSameOrigin(serverRequest)) {
|
||||
logger.trace("Skip: request is from same origin");
|
||||
return true;
|
||||
}
|
||||
|
||||
boolean preFlightRequest = CorsUtils.isPreFlightRequest(request);
|
||||
if (config == null) {
|
||||
if (preFlightRequest) {
|
||||
rejectRequest(serverResponse);
|
||||
rejectRequest(new ServletServerHttpResponse(response));
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
@@ -89,17 +83,7 @@ public class DefaultCorsProcessor implements CorsProcessor {
|
||||
}
|
||||
}
|
||||
|
||||
return handleInternal(serverRequest, serverResponse, config, preFlightRequest);
|
||||
}
|
||||
|
||||
private boolean responseHasCors(ServerHttpResponse response) {
|
||||
try {
|
||||
return (response.getHeaders().getAccessControlAllowOrigin() != null);
|
||||
}
|
||||
catch (NullPointerException npe) {
|
||||
// SPR-11919 and https://issues.jboss.org/browse/WFLY-3474
|
||||
return false;
|
||||
}
|
||||
return handleInternal(new ServletServerHttpRequest(request), new ServletServerHttpResponse(response), config, preFlightRequest);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -110,6 +94,7 @@ public class DefaultCorsProcessor implements CorsProcessor {
|
||||
protected void rejectRequest(ServerHttpResponse response) throws IOException {
|
||||
response.setStatusCode(HttpStatus.FORBIDDEN);
|
||||
response.getBody().write("Invalid CORS request".getBytes(StandardCharsets.UTF_8));
|
||||
response.flush();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -122,9 +107,6 @@ public class DefaultCorsProcessor implements CorsProcessor {
|
||||
String allowOrigin = checkOrigin(config, requestOrigin);
|
||||
HttpHeaders responseHeaders = response.getHeaders();
|
||||
|
||||
responseHeaders.addAll(HttpHeaders.VARY, Arrays.asList(HttpHeaders.ORIGIN,
|
||||
HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, HttpHeaders.ACCESS_CONTROL_REQUEST_HEADERS));
|
||||
|
||||
if (allowOrigin == null) {
|
||||
logger.debug("Reject: '" + requestOrigin + "' origin is not allowed");
|
||||
rejectRequest(response);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -36,18 +36,21 @@ import org.springframework.web.util.UriComponentsBuilder;
|
||||
public abstract class CorsUtils {
|
||||
|
||||
/**
|
||||
* Returns {@code true} if the request is a valid CORS one.
|
||||
* Returns {@code true} if the request is a valid CORS one by checking {@code Origin}
|
||||
* header presence and ensuring that origins are different via {@link #isSameOrigin}.
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public static boolean isCorsRequest(ServerHttpRequest request) {
|
||||
return (request.getHeaders().get(HttpHeaders.ORIGIN) != null);
|
||||
return request.getHeaders().containsKey(HttpHeaders.ORIGIN) && !isSameOrigin(request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@code true} if the request is a valid CORS pre-flight one.
|
||||
* To be used in combination with {@link #isCorsRequest(ServerHttpRequest)} since
|
||||
* regular CORS checks are not invoked here for performance reasons.
|
||||
*/
|
||||
public static boolean isPreFlightRequest(ServerHttpRequest request) {
|
||||
return (request.getMethod() == HttpMethod.OPTIONS && isCorsRequest(request) &&
|
||||
request.getHeaders().get(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD) != null);
|
||||
return (request.getMethod() == HttpMethod.OPTIONS && request.getHeaders().containsKey(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -61,7 +64,9 @@ public abstract class CorsUtils {
|
||||
*
|
||||
* @return {@code true} if the request is a same-origin one, {@code false} in case
|
||||
* of a cross-origin request
|
||||
* @deprecated as of 5.2, same-origin checks are performed directly by {@link #isCorsRequest}
|
||||
*/
|
||||
@Deprecated
|
||||
public static boolean isSameOrigin(ServerHttpRequest request) {
|
||||
String origin = request.getHeaders().getOrigin();
|
||||
if (origin == null) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -75,14 +75,10 @@ public class CorsWebFilter implements WebFilter {
|
||||
@Override
|
||||
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
|
||||
ServerHttpRequest request = exchange.getRequest();
|
||||
if (CorsUtils.isCorsRequest(request)) {
|
||||
CorsConfiguration corsConfiguration = this.configSource.getCorsConfiguration(exchange);
|
||||
if (corsConfiguration != null) {
|
||||
boolean isValid = this.processor.process(corsConfiguration, exchange);
|
||||
if (!isValid || CorsUtils.isPreFlightRequest(request)) {
|
||||
return Mono.empty();
|
||||
}
|
||||
}
|
||||
CorsConfiguration corsConfiguration = this.configSource.getCorsConfiguration(exchange);
|
||||
boolean isValid = this.processor.process(corsConfiguration, exchange);
|
||||
if (!isValid || CorsUtils.isPreFlightRequest(request)) {
|
||||
return Mono.empty();
|
||||
}
|
||||
return chain.filter(exchange);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -40,8 +40,7 @@ import org.springframework.web.server.ServerWebExchange;
|
||||
* <p>Note that when input {@link CorsConfiguration} is {@code null}, this
|
||||
* implementation does not reject simple or actual requests outright but simply
|
||||
* avoid adding CORS headers to the response. CORS processing is also skipped
|
||||
* if the response already contains CORS headers, or if the request is detected
|
||||
* as a same-origin one.
|
||||
* if the response already contains CORS headers.
|
||||
*
|
||||
* @author Sebastien Deleuze
|
||||
* @author Rossen Stoyanchev
|
||||
@@ -51,27 +50,26 @@ public class DefaultCorsProcessor implements CorsProcessor {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(DefaultCorsProcessor.class);
|
||||
|
||||
private static final List<String> VARY_HEADERS = Arrays.asList(
|
||||
HttpHeaders.ORIGIN, HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, HttpHeaders.ACCESS_CONTROL_REQUEST_HEADERS);
|
||||
|
||||
|
||||
@Override
|
||||
public boolean process(@Nullable CorsConfiguration config, ServerWebExchange exchange) {
|
||||
|
||||
ServerHttpRequest request = exchange.getRequest();
|
||||
ServerHttpResponse response = exchange.getResponse();
|
||||
response.getHeaders().addAll(HttpHeaders.VARY, VARY_HEADERS);
|
||||
|
||||
if (!CorsUtils.isCorsRequest(request)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (responseHasCors(response)) {
|
||||
if (response.getHeaders().getFirst(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN) != null) {
|
||||
logger.trace("Skip: response already contains \"Access-Control-Allow-Origin\"");
|
||||
return true;
|
||||
}
|
||||
|
||||
if (CorsUtils.isSameOrigin(request)) {
|
||||
logger.trace("Skip: request is from same origin");
|
||||
return true;
|
||||
}
|
||||
|
||||
boolean preFlightRequest = CorsUtils.isPreFlightRequest(request);
|
||||
if (config == null) {
|
||||
if (preFlightRequest) {
|
||||
@@ -86,10 +84,6 @@ public class DefaultCorsProcessor implements CorsProcessor {
|
||||
return handleInternal(exchange, config, preFlightRequest);
|
||||
}
|
||||
|
||||
private boolean responseHasCors(ServerHttpResponse response) {
|
||||
return response.getHeaders().getFirst(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN) != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoked when one of the CORS checks failed.
|
||||
*/
|
||||
@@ -107,9 +101,6 @@ public class DefaultCorsProcessor implements CorsProcessor {
|
||||
ServerHttpResponse response = exchange.getResponse();
|
||||
HttpHeaders responseHeaders = response.getHeaders();
|
||||
|
||||
response.getHeaders().addAll(HttpHeaders.VARY, Arrays.asList(HttpHeaders.ORIGIN,
|
||||
HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, HttpHeaders.ACCESS_CONTROL_REQUEST_HEADERS));
|
||||
|
||||
String requestOrigin = request.getHeaders().getOrigin();
|
||||
String allowOrigin = checkOrigin(config, requestOrigin);
|
||||
if (allowOrigin == null) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2016 the original author or authors.
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -83,16 +83,11 @@ public class CorsFilter extends OncePerRequestFilter {
|
||||
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
|
||||
FilterChain filterChain) throws ServletException, IOException {
|
||||
|
||||
if (CorsUtils.isCorsRequest(request)) {
|
||||
CorsConfiguration corsConfiguration = this.configSource.getCorsConfiguration(request);
|
||||
if (corsConfiguration != null) {
|
||||
boolean isValid = this.processor.processRequest(corsConfiguration, request, response);
|
||||
if (!isValid || CorsUtils.isPreFlightRequest(request)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
CorsConfiguration corsConfiguration = this.configSource.getCorsConfiguration(request);
|
||||
boolean isValid = this.processor.processRequest(corsConfiguration, request, response);
|
||||
if (!isValid || CorsUtils.isPreFlightRequest(request)) {
|
||||
return;
|
||||
}
|
||||
|
||||
filterChain.doFilter(request, response);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2015 the original author or authors.
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -62,11 +62,6 @@ public class CorsUtilsTests {
|
||||
request.setMethod(HttpMethod.OPTIONS.name());
|
||||
request.addHeader(HttpHeaders.ORIGIN, "https://domain.com");
|
||||
assertFalse(CorsUtils.isPreFlightRequest(request));
|
||||
|
||||
request = new MockHttpServletRequest();
|
||||
request.setMethod(HttpMethod.OPTIONS.name());
|
||||
request.addHeader(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, "GET");
|
||||
assertFalse(CorsUtils.isPreFlightRequest(request));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -51,13 +51,35 @@ public class DefaultCorsProcessorTests {
|
||||
public void setup() {
|
||||
this.request = new MockHttpServletRequest();
|
||||
this.request.setRequestURI("/test.html");
|
||||
this.request.setRemoteHost("domain1.com");
|
||||
this.request.setServerName("domain1.com");
|
||||
this.conf = new CorsConfiguration();
|
||||
this.response = new MockHttpServletResponse();
|
||||
this.response.setStatus(HttpServletResponse.SC_OK);
|
||||
this.processor = new DefaultCorsProcessor();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void requestWithoutOriginHeader() throws Exception {
|
||||
this.request.setMethod(HttpMethod.GET.name());
|
||||
|
||||
this.processor.processRequest(this.conf, this.request, this.response);
|
||||
assertFalse(this.response.containsHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN));
|
||||
assertThat(this.response.getHeaders(HttpHeaders.VARY), contains(HttpHeaders.ORIGIN,
|
||||
HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, HttpHeaders.ACCESS_CONTROL_REQUEST_HEADERS));
|
||||
assertEquals(HttpServletResponse.SC_OK, this.response.getStatus());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void sameOriginRequest() throws Exception {
|
||||
this.request.setMethod(HttpMethod.GET.name());
|
||||
this.request.addHeader(HttpHeaders.ORIGIN, "http://domain1.com");
|
||||
|
||||
this.processor.processRequest(this.conf, this.request, this.response);
|
||||
assertFalse(this.response.containsHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN));
|
||||
assertThat(this.response.getHeaders(HttpHeaders.VARY), contains(HttpHeaders.ORIGIN,
|
||||
HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, HttpHeaders.ACCESS_CONTROL_REQUEST_HEADERS));
|
||||
assertEquals(HttpServletResponse.SC_OK, this.response.getStatus());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void actualRequestWithOriginHeader() throws Exception {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -39,7 +39,7 @@ public class CorsUtilsTests {
|
||||
|
||||
@Test
|
||||
public void isCorsRequest() {
|
||||
ServerHttpRequest request = get("/").header(HttpHeaders.ORIGIN, "https://domain.com").build();
|
||||
ServerHttpRequest request = get("http://domain.com/").header(HttpHeaders.ORIGIN, "https://domain.com").build();
|
||||
assertTrue(CorsUtils.isCorsRequest(request));
|
||||
}
|
||||
|
||||
@@ -65,9 +65,6 @@ public class CorsUtilsTests {
|
||||
|
||||
request = options("/").header(HttpHeaders.ORIGIN, "https://domain.com").build();
|
||||
assertFalse(CorsUtils.isPreFlightRequest(request));
|
||||
|
||||
request = options("/").header(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, "GET").build();
|
||||
assertFalse(CorsUtils.isPreFlightRequest(request));
|
||||
}
|
||||
|
||||
@Test // SPR-16262
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2017 the original author or authors.
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -63,6 +63,46 @@ public class CorsWebFilterTests {
|
||||
filter = new CorsWebFilter(r -> config);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void nonCorsRequest() {
|
||||
WebFilterChain filterChain = (filterExchange) -> {
|
||||
try {
|
||||
HttpHeaders headers = filterExchange.getResponse().getHeaders();
|
||||
assertNull(headers.getFirst(ACCESS_CONTROL_ALLOW_ORIGIN));
|
||||
assertNull(headers.getFirst(ACCESS_CONTROL_EXPOSE_HEADERS));
|
||||
} catch (AssertionError ex) {
|
||||
return Mono.error(ex);
|
||||
}
|
||||
return Mono.empty();
|
||||
|
||||
};
|
||||
MockServerWebExchange exchange = MockServerWebExchange.from(
|
||||
MockServerHttpRequest
|
||||
.get("https://domain1.com/test.html")
|
||||
.header(HOST, "domain1.com"));
|
||||
this.filter.filter(exchange, filterChain).block();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void sameOriginRequest() {
|
||||
WebFilterChain filterChain = (filterExchange) -> {
|
||||
try {
|
||||
HttpHeaders headers = filterExchange.getResponse().getHeaders();
|
||||
assertNull(headers.getFirst(ACCESS_CONTROL_ALLOW_ORIGIN));
|
||||
assertNull(headers.getFirst(ACCESS_CONTROL_EXPOSE_HEADERS));
|
||||
} catch (AssertionError ex) {
|
||||
return Mono.error(ex);
|
||||
}
|
||||
return Mono.empty();
|
||||
|
||||
};
|
||||
MockServerWebExchange exchange = MockServerWebExchange.from(
|
||||
MockServerHttpRequest
|
||||
.get("https://domain1.com/test.html")
|
||||
.header(ORIGIN, "https://domain1.com"));
|
||||
this.filter.filter(exchange, filterChain).block();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void validActualRequest() {
|
||||
WebFilterChain filterChain = (filterExchange) -> {
|
||||
@@ -82,7 +122,7 @@ public class CorsWebFilterTests {
|
||||
.header(HOST, "domain1.com")
|
||||
.header(ORIGIN, "https://domain2.com")
|
||||
.header("header2", "foo"));
|
||||
this.filter.filter(exchange, filterChain);
|
||||
this.filter.filter(exchange, filterChain).block();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -96,8 +136,7 @@ public class CorsWebFilterTests {
|
||||
|
||||
WebFilterChain filterChain = (filterExchange) -> Mono.error(
|
||||
new AssertionError("Invalid requests must not be forwarded to the filter chain"));
|
||||
filter.filter(exchange, filterChain);
|
||||
|
||||
filter.filter(exchange, filterChain).block();
|
||||
assertNull(exchange.getResponse().getHeaders().getFirst(ACCESS_CONTROL_ALLOW_ORIGIN));
|
||||
}
|
||||
|
||||
@@ -115,7 +154,7 @@ public class CorsWebFilterTests {
|
||||
|
||||
WebFilterChain filterChain = (filterExchange) -> Mono.error(
|
||||
new AssertionError("Preflight requests must not be forwarded to the filter chain"));
|
||||
filter.filter(exchange, filterChain);
|
||||
filter.filter(exchange, filterChain).block();
|
||||
|
||||
HttpHeaders headers = exchange.getResponse().getHeaders();
|
||||
assertEquals("https://domain2.com", headers.getFirst(ACCESS_CONTROL_ALLOW_ORIGIN));
|
||||
@@ -138,7 +177,7 @@ public class CorsWebFilterTests {
|
||||
WebFilterChain filterChain = (filterExchange) -> Mono.error(
|
||||
new AssertionError("Preflight requests must not be forwarded to the filter chain"));
|
||||
|
||||
filter.filter(exchange, filterChain);
|
||||
filter.filter(exchange, filterChain).block();
|
||||
|
||||
assertNull(exchange.getResponse().getHeaders().getFirst(ACCESS_CONTROL_ALLOW_ORIGIN));
|
||||
}
|
||||
|
||||
@@ -60,6 +60,37 @@ public class DefaultCorsProcessorTests {
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void requestWithoutOriginHeader() throws Exception {
|
||||
MockServerHttpRequest request = MockServerHttpRequest
|
||||
.method(HttpMethod.GET, "http://domain1.com/test.html")
|
||||
.build();
|
||||
ServerWebExchange exchange = MockServerWebExchange.from(request);
|
||||
this.processor.process(this.conf, exchange);
|
||||
|
||||
ServerHttpResponse response = exchange.getResponse();
|
||||
assertFalse(response.getHeaders().containsKey(ACCESS_CONTROL_ALLOW_ORIGIN));
|
||||
assertThat(response.getHeaders().get(VARY), contains(ORIGIN,
|
||||
ACCESS_CONTROL_REQUEST_METHOD, ACCESS_CONTROL_REQUEST_HEADERS));
|
||||
assertNull(response.getStatusCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void sameOriginRequest() throws Exception {
|
||||
MockServerHttpRequest request = MockServerHttpRequest
|
||||
.method(HttpMethod.GET, "http://domain1.com/test.html")
|
||||
.header(HttpHeaders.ORIGIN, "http://domain1.com")
|
||||
.build();
|
||||
ServerWebExchange exchange = MockServerWebExchange.from(request);
|
||||
this.processor.process(this.conf, exchange);
|
||||
|
||||
ServerHttpResponse response = exchange.getResponse();
|
||||
assertFalse(response.getHeaders().containsKey(ACCESS_CONTROL_ALLOW_ORIGIN));
|
||||
assertThat(response.getHeaders().get(VARY), contains(ORIGIN,
|
||||
ACCESS_CONTROL_REQUEST_METHOD, ACCESS_CONTROL_REQUEST_HEADERS));
|
||||
assertNull(response.getStatusCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void actualRequestWithOriginHeader() throws Exception {
|
||||
ServerWebExchange exchange = actualRequest();
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2015 the original author or authors.
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -52,6 +52,36 @@ public class CorsFilterTests {
|
||||
filter = new CorsFilter(r -> config);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void nonCorsRequest() throws ServletException, IOException {
|
||||
|
||||
MockHttpServletRequest request = new MockHttpServletRequest(HttpMethod.GET.name(), "/test.html");
|
||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
|
||||
FilterChain filterChain = (filterRequest, filterResponse) -> {
|
||||
assertNull(response.getHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN));
|
||||
assertNull(response.getHeader(HttpHeaders.ACCESS_CONTROL_EXPOSE_HEADERS));
|
||||
};
|
||||
filter.doFilter(request, response, filterChain);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void sameOriginRequest() throws ServletException, IOException {
|
||||
|
||||
MockHttpServletRequest request = new MockHttpServletRequest(HttpMethod.GET.name(), "https://domain1.com/test.html");
|
||||
request.addHeader(HttpHeaders.ORIGIN, "https://domain1.com");
|
||||
request.setScheme("https");
|
||||
request.setServerName("domain1.com");
|
||||
request.setServerPort(443);
|
||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
|
||||
FilterChain filterChain = (filterRequest, filterResponse) -> {
|
||||
assertNull(response.getHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN));
|
||||
assertNull(response.getHeader(HttpHeaders.ACCESS_CONTROL_EXPOSE_HEADERS));
|
||||
};
|
||||
filter.doFilter(request, response, filterChain);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void validActualRequest() throws ServletException, IOException {
|
||||
|
||||
|
||||
Reference in New Issue
Block a user