diff --git a/config/src/test/groovy/org/springframework/security/config/http/HttpCorsConfigTests.groovy b/config/src/test/groovy/org/springframework/security/config/http/HttpCorsConfigTests.groovy
deleted file mode 100644
index f18f142173..0000000000
--- a/config/src/test/groovy/org/springframework/security/config/http/HttpCorsConfigTests.groovy
+++ /dev/null
@@ -1,171 +0,0 @@
-/*
- * Copyright 2002-2016 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. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
- * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations under the License.
- */
-package org.springframework.security.config.http
-
-import org.springframework.beans.factory.BeanCreationException
-
-import javax.servlet.http.HttpServletResponse
-
-import org.springframework.http.*
-import org.springframework.mock.web.*
-import org.springframework.security.web.authentication.Http403ForbiddenEntryPoint
-import org.springframework.web.bind.annotation.*
-import org.springframework.web.filter.CorsFilter
-import org.springframework.web.cors.CorsConfiguration
-import org.springframework.web.cors.UrlBasedCorsConfigurationSource
-
-/**
- *
- * @author Rob Winch
- * @author Tim Ysewyn
- */
-class HttpCorsConfigTests extends AbstractHttpConfigTests {
- MockHttpServletRequest request
- MockHttpServletResponse response
- MockFilterChain chain
-
- def setup() {
- request = new MockHttpServletRequest(method:"GET")
- response = new MockHttpServletResponse()
- chain = new MockFilterChain()
- }
-
- def "No MVC throws meaningful error"() {
- when:
- xml.http('entry-point-ref' : 'ep') {
- 'cors'()
- 'intercept-url'(pattern:'/**', access: 'authenticated')
- }
- bean('ep', Http403ForbiddenEntryPoint)
- createAppContext()
- then:
- BeanCreationException success = thrown()
- success.message.contains("Please ensure Spring Security & Spring MVC are configured in a shared ApplicationContext")
- }
-
- def "HandlerMappingIntrospector explicit"() {
- setup:
- xml.http('entry-point-ref' : 'ep') {
- 'cors'()
- 'intercept-url'(pattern:'/**', access: 'authenticated')
- }
- bean('ep', Http403ForbiddenEntryPoint)
- bean('controller', CorsController)
- xml.'mvc:annotation-driven'()
- createAppContext()
- when:
- addCors()
- springSecurityFilterChain.doFilter(request,response,chain)
- then: 'Ensure we a CORS response w/ Spring Security headers too'
- responseHeaders['Access-Control-Allow-Origin']
- responseHeaders['X-Content-Type-Options']
- when:
- setup()
- addCors(true)
- springSecurityFilterChain.doFilter(request,response,chain)
- then: 'Ensure we a CORS response w/ Spring Security headers too'
- responseHeaders['Access-Control-Allow-Origin']
- responseHeaders['X-Content-Type-Options']
- response.status == HttpServletResponse.SC_OK
- }
-
- def "CorsConfigurationSource"() {
- setup:
- xml.http('entry-point-ref' : 'ep') {
- 'cors'('configuration-source-ref':'ccs')
- 'intercept-url'(pattern:'/**', access: 'authenticated')
- }
- bean('ep', Http403ForbiddenEntryPoint)
- bean('ccs', MyCorsConfigurationSource)
- createAppContext()
- when:
- addCors()
- springSecurityFilterChain.doFilter(request,response,chain)
- then: 'Ensure we a CORS response w/ Spring Security headers too'
- responseHeaders['Access-Control-Allow-Origin']
- responseHeaders['X-Content-Type-Options']
- when:
- setup()
- addCors(true)
- springSecurityFilterChain.doFilter(request,response,chain)
- then: 'Ensure we a CORS response w/ Spring Security headers too'
- responseHeaders['Access-Control-Allow-Origin']
- responseHeaders['X-Content-Type-Options']
- response.status == HttpServletResponse.SC_OK
- }
-
- def "CorsFilter"() {
- setup:
- xml.http('entry-point-ref' : 'ep') {
- 'cors'('ref' : 'cf')
- 'intercept-url'(pattern:'/**', access: 'authenticated')
- }
- xml.'b:bean'(id: 'cf', 'class': CorsFilter.name) {
- 'b:constructor-arg'(ref: 'ccs')
- }
- bean('ep', Http403ForbiddenEntryPoint)
- bean('ccs', MyCorsConfigurationSource)
- createAppContext()
- when:
- addCors()
- springSecurityFilterChain.doFilter(request,response,chain)
- then: 'Ensure we a CORS response w/ Spring Security headers too'
- responseHeaders['Access-Control-Allow-Origin']
- responseHeaders['X-Content-Type-Options']
- when:
- setup()
- addCors(true)
- springSecurityFilterChain.doFilter(request,response,chain)
- then: 'Ensure we a CORS response w/ Spring Security headers too'
- responseHeaders['Access-Control-Allow-Origin']
- responseHeaders['X-Content-Type-Options']
- response.status == HttpServletResponse.SC_OK
- }
-
- def addCors(boolean isPreflight=false) {
- request.addHeader(HttpHeaders.ORIGIN,"https://example.com")
- if(!isPreflight) {
- return
- }
- request.method = HttpMethod.OPTIONS.name()
- request.addHeader(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, HttpMethod.POST.name())
- }
-
- def getResponseHeaders() {
- def headers = [:]
- response.headerNames.each { name ->
- headers.put(name, response.getHeaderValues(name).join(','))
- }
- return headers
- }
-
- @RestController
- @CrossOrigin(methods = [
- RequestMethod.GET, RequestMethod.POST
- ])
- static class CorsController {
- @RequestMapping("/")
- String hello() {
- "Hello"
- }
- }
-
- static class MyCorsConfigurationSource extends UrlBasedCorsConfigurationSource {
- MyCorsConfigurationSource() {
- registerCorsConfiguration('/**', new CorsConfiguration(allowedOrigins : ['*'], allowedMethods : [
- RequestMethod.GET.name(),
- RequestMethod.POST.name()
- ]))
- }
- }
-}
diff --git a/config/src/test/java/org/springframework/security/config/http/HttpCorsConfigTests.java b/config/src/test/java/org/springframework/security/config/http/HttpCorsConfigTests.java
new file mode 100644
index 0000000000..52c3245a12
--- /dev/null
+++ b/config/src/test/java/org/springframework/security/config/http/HttpCorsConfigTests.java
@@ -0,0 +1,168 @@
+/*
+ * Copyright 2002-2018 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.springframework.security.config.http;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.springframework.beans.factory.BeanCreationException;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpMethod;
+import org.springframework.security.config.test.SpringTestRule;
+import org.springframework.test.web.servlet.MockMvc;
+import org.springframework.test.web.servlet.ResultMatcher;
+import org.springframework.test.web.servlet.request.RequestPostProcessor;
+import org.springframework.web.bind.annotation.CrossOrigin;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.cors.CorsConfiguration;
+import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
+
+import java.util.Arrays;
+
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.options;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
+
+/**
+ *
+ * @author Rob Winch
+ * @author Tim Ysewyn
+ * @author Josh Cummings
+ */
+public class HttpCorsConfigTests {
+
+ private static final String CONFIG_LOCATION_PREFIX =
+ "classpath:org/springframework/security/config/http/HttpCorsConfigTests";
+
+ @Rule
+ public final SpringTestRule spring = new SpringTestRule();
+
+ @Autowired
+ MockMvc mvc;
+
+ @Test
+ public void autowireWhenMissingMvcThenGivesInformativeError() {
+ assertThatThrownBy(() ->
+ this.spring.configLocations(this.xml("RequiresMvc")).autowire())
+ .isInstanceOf(BeanCreationException.class)
+ .hasMessageContaining("Please ensure Spring Security & Spring MVC are configured in a shared ApplicationContext");
+ }
+
+ @Test
+ public void getWhenUsingCorsThenDoesSpringSecurityCorsHandshake()
+ throws Exception {
+
+ this.spring.configLocations(this.xml("WithCors")).autowire();
+
+ this.mvc.perform(get("/").with(this.approved()))
+ .andExpect(corsResponseHeaders())
+ .andExpect((status().isIAmATeapot()));
+
+ this.mvc.perform(options("/").with(this.preflight()))
+ .andExpect(corsResponseHeaders())
+ .andExpect(status().isOk());
+ }
+
+ @Test
+ public void getWhenUsingCustomCorsConfigurationSourceThenDoesSpringSecurityCorsHandshake()
+ throws Exception {
+
+ this.spring.configLocations(this.xml("WithCorsConfigurationSource")).autowire();
+
+ this.mvc.perform(get("/").with(this.approved()))
+ .andExpect(corsResponseHeaders())
+ .andExpect((status().isIAmATeapot()));
+
+ this.mvc.perform(options("/").with(this.preflight()))
+ .andExpect(corsResponseHeaders())
+ .andExpect(status().isOk());
+ }
+
+ @Test
+ public void getWhenUsingCustomCorsFilterThenDoesSPringSecurityCorsHandshake()
+ throws Exception {
+
+ this.spring.configLocations(this.xml("WithCorsFilter")).autowire();
+
+ this.mvc.perform(get("/").with(this.approved()))
+ .andExpect(corsResponseHeaders())
+ .andExpect((status().isIAmATeapot()));
+
+ this.mvc.perform(options("/").with(this.preflight()))
+ .andExpect(corsResponseHeaders())
+ .andExpect(status().isOk());
+ }
+
+ @RestController
+ @CrossOrigin(methods = {
+ RequestMethod.GET, RequestMethod.POST
+ })
+ static class CorsController {
+ @RequestMapping("/")
+ String hello() {
+ return "Hello";
+ }
+ }
+
+ static class MyCorsConfigurationSource extends UrlBasedCorsConfigurationSource {
+ MyCorsConfigurationSource() {
+ CorsConfiguration configuration = new CorsConfiguration();
+ configuration.setAllowedOrigins(Arrays.asList("*"));
+ configuration.setAllowedMethods(Arrays.asList(RequestMethod.GET.name(), RequestMethod.POST.name()));
+
+ super.registerCorsConfiguration(
+ "/**",
+ configuration);
+ }
+ }
+
+ private String xml(String configName) {
+ return CONFIG_LOCATION_PREFIX + "-" + configName + ".xml";
+ }
+
+ private RequestPostProcessor preflight() {
+ return cors(true);
+ }
+
+ private RequestPostProcessor approved() {
+ return cors(false);
+ }
+
+ private RequestPostProcessor cors(boolean preflight) {
+ return (request) -> {
+ request.addHeader(HttpHeaders.ORIGIN, "https://example.com");
+
+ if ( preflight ) {
+ request.setMethod(HttpMethod.OPTIONS.name());
+ request.addHeader(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, HttpMethod.POST.name());
+ }
+
+ return request;
+ };
+ }
+
+ private ResultMatcher corsResponseHeaders() {
+ return result -> {
+ header().exists("Access-Control-Allow-Origin").match(result);
+ header().exists("X-Content-Type-Options").match(result);
+ };
+ }
+
+}
diff --git a/config/src/test/resources/org/springframework/security/config/http/HttpCorsConfigTests-RequiresMvc.xml b/config/src/test/resources/org/springframework/security/config/http/HttpCorsConfigTests-RequiresMvc.xml
new file mode 100644
index 0000000000..9a66ab3a5b
--- /dev/null
+++ b/config/src/test/resources/org/springframework/security/config/http/HttpCorsConfigTests-RequiresMvc.xml
@@ -0,0 +1,31 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/config/src/test/resources/org/springframework/security/config/http/HttpCorsConfigTests-WithCors.xml b/config/src/test/resources/org/springframework/security/config/http/HttpCorsConfigTests-WithCors.xml
new file mode 100644
index 0000000000..ac57e95b45
--- /dev/null
+++ b/config/src/test/resources/org/springframework/security/config/http/HttpCorsConfigTests-WithCors.xml
@@ -0,0 +1,45 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/config/src/test/resources/org/springframework/security/config/http/HttpCorsConfigTests-WithCorsConfigurationSource.xml b/config/src/test/resources/org/springframework/security/config/http/HttpCorsConfigTests-WithCorsConfigurationSource.xml
new file mode 100644
index 0000000000..b73f227634
--- /dev/null
+++ b/config/src/test/resources/org/springframework/security/config/http/HttpCorsConfigTests-WithCorsConfigurationSource.xml
@@ -0,0 +1,39 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/config/src/test/resources/org/springframework/security/config/http/HttpCorsConfigTests-WithCorsFilter.xml b/config/src/test/resources/org/springframework/security/config/http/HttpCorsConfigTests-WithCorsFilter.xml
new file mode 100644
index 0000000000..28daed34b1
--- /dev/null
+++ b/config/src/test/resources/org/springframework/security/config/http/HttpCorsConfigTests-WithCorsFilter.xml
@@ -0,0 +1,43 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+