瀏覽代碼

Allow configuration of cors through nested builder

Issue: gh-5557
Eleftheria Stein 6 年之前
父節點
當前提交
6fd515813c

+ 30 - 0
config/src/main/java/org/springframework/security/config/annotation/web/builders/HttpSecurity.java

@@ -448,6 +448,36 @@ public final class HttpSecurity extends
 		return getOrApply(new CorsConfigurer<>());
 	}
 
+	/**
+	 * Adds a {@link CorsFilter} to be used. If a bean by the name of corsFilter is
+	 * provided, that {@link CorsFilter} is used. Else if corsConfigurationSource is
+	 * defined, then that {@link CorsConfiguration} is used. Otherwise, if Spring MVC is
+	 * on the classpath a {@link HandlerMappingIntrospector} is used.
+	 * You can enable CORS using:
+	 *
+	 * <pre>
+	 * &#064;Configuration
+	 * &#064;EnableWebSecurity
+	 * public class CorsSecurityConfig extends WebSecurityConfigurerAdapter {
+	 *
+	 * 	&#064;Override
+	 *     protected void configure(HttpSecurity http) throws Exception {
+	 *         http
+	 *             .cors(withDefaults());
+	 *     }
+	 * }
+	 * </pre>
+	 *
+	 * @param corsCustomizer the {@link Customizer} to provide more options for
+	 * the {@link CorsConfigurer}
+	 * @return the {@link HttpSecurity} for further customizations
+	 * @throws Exception
+	 */
+	public HttpSecurity cors(Customizer<CorsConfigurer<HttpSecurity>> corsCustomizer) throws Exception {
+		corsCustomizer.customize(getOrApply(new CorsConfigurer<>()));
+		return HttpSecurity.this;
+	}
+
 	/**
 	 * Allows configuring of Session Management.
 	 *

+ 150 - 0
config/src/test/java/org/springframework/security/config/annotation/web/configurers/CorsConfigurerTests.java

@@ -42,6 +42,7 @@ import java.util.Arrays;
 import java.util.Collections;
 
 import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import static org.springframework.security.config.Customizer.withDefaults;
 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;
@@ -131,6 +132,55 @@ public class CorsConfigurerTests {
 		}
 	}
 
+	@Test
+	public void getWhenDefaultsInLambdaAndCrossOriginAnnotationThenRespondsWithCorsHeaders() throws Exception {
+		this.spring.register(MvcCorsInLambdaConfig.class).autowire();
+
+		this.mvc.perform(get("/")
+				.header(HttpHeaders.ORIGIN, "https://example.com"))
+				.andExpect(header().exists("Access-Control-Allow-Origin"))
+				.andExpect(header().exists("X-Content-Type-Options"));
+	}
+
+	@Test
+	public void optionsWhenDefaultsInLambdaAndCrossOriginAnnotationThenRespondsWithCorsHeaders() throws Exception {
+		this.spring.register(MvcCorsInLambdaConfig.class).autowire();
+
+		this.mvc.perform(options("/")
+				.header(org.springframework.http.HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, HttpMethod.POST.name())
+				.header(HttpHeaders.ORIGIN, "https://example.com"))
+				.andExpect(status().isOk())
+				.andExpect(header().exists("Access-Control-Allow-Origin"))
+				.andExpect(header().exists("X-Content-Type-Options"));
+	}
+
+	@EnableWebMvc
+	@EnableWebSecurity
+	static class MvcCorsInLambdaConfig extends WebSecurityConfigurerAdapter {
+
+		@Override
+		protected void configure(HttpSecurity http) throws Exception {
+			// @formatter:off
+			http
+				.authorizeRequests()
+					.anyRequest().authenticated()
+					.and()
+				.cors(withDefaults());
+			// @formatter:on
+		}
+
+		@RestController
+		@CrossOrigin(methods = {
+				RequestMethod.GET, RequestMethod.POST
+		})
+		static class CorsController {
+			@RequestMapping("/")
+			String hello() {
+				return "Hello";
+			}
+		}
+	}
+
 	@Test
 	public void getWhenCorsConfigurationSourceBeanThenRespondsWithCorsHeaders() throws Exception {
 		this.spring.register(ConfigSourceConfig.class).autowire();
@@ -180,6 +230,57 @@ public class CorsConfigurerTests {
 		}
 	}
 
+	@Test
+	public void getWhenMvcCorsInLambdaConfigAndCorsConfigurationSourceBeanThenRespondsWithCorsHeaders()
+			throws Exception {
+		this.spring.register(ConfigSourceInLambdaConfig.class).autowire();
+
+		this.mvc.perform(get("/")
+				.header(HttpHeaders.ORIGIN, "https://example.com"))
+				.andExpect(header().exists("Access-Control-Allow-Origin"))
+				.andExpect(header().exists("X-Content-Type-Options"));
+	}
+
+	@Test
+	public void optionsWhenMvcCorsInLambdaConfigAndCorsConfigurationSourceBeanThenRespondsWithCorsHeaders()
+			throws Exception {
+		this.spring.register(ConfigSourceInLambdaConfig.class).autowire();
+
+		this.mvc.perform(options("/")
+				.header(org.springframework.http.HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, HttpMethod.POST.name())
+				.header(HttpHeaders.ORIGIN, "https://example.com"))
+				.andExpect(status().isOk())
+				.andExpect(header().exists("Access-Control-Allow-Origin"))
+				.andExpect(header().exists("X-Content-Type-Options"));
+	}
+
+	@EnableWebSecurity
+	static class ConfigSourceInLambdaConfig extends WebSecurityConfigurerAdapter {
+
+		@Override
+		protected void configure(HttpSecurity http) throws Exception {
+			// @formatter:off
+			http
+				.authorizeRequests()
+					.anyRequest().authenticated()
+					.and()
+				.cors(withDefaults());
+			// @formatter:on
+		}
+
+		@Bean
+		CorsConfigurationSource corsConfigurationSource() {
+			UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
+			CorsConfiguration corsConfiguration = new CorsConfiguration();
+			corsConfiguration.setAllowedOrigins(Collections.singletonList("*"));
+			corsConfiguration.setAllowedMethods(Arrays.asList(
+					RequestMethod.GET.name(),
+					RequestMethod.POST.name()));
+			source.registerCorsConfiguration("/**", corsConfiguration);
+			return source;
+		}
+	}
+
 	@Test
 	public void getWhenCorsFilterBeanThenRespondsWithCorsHeaders() throws Exception {
 		this.spring.register(CorsFilterConfig.class).autowire();
@@ -228,4 +329,53 @@ public class CorsConfigurerTests {
 			return new CorsFilter(source);
 		}
 	}
+
+	@Test
+	public void getWhenConfigSourceInLambdaConfigAndCorsFilterBeanThenRespondsWithCorsHeaders() throws Exception {
+		this.spring.register(CorsFilterInLambdaConfig.class).autowire();
+
+		this.mvc.perform(get("/")
+				.header(HttpHeaders.ORIGIN, "https://example.com"))
+				.andExpect(header().exists("Access-Control-Allow-Origin"))
+				.andExpect(header().exists("X-Content-Type-Options"));
+	}
+
+	@Test
+	public void optionsWhenConfigSourceInLambdaConfigAndCorsFilterBeanThenRespondsWithCorsHeaders() throws Exception {
+		this.spring.register(CorsFilterInLambdaConfig.class).autowire();
+
+		this.mvc.perform(options("/")
+				.header(org.springframework.http.HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, HttpMethod.POST.name())
+				.header(HttpHeaders.ORIGIN, "https://example.com"))
+				.andExpect(status().isOk())
+				.andExpect(header().exists("Access-Control-Allow-Origin"))
+				.andExpect(header().exists("X-Content-Type-Options"));
+	}
+
+	@EnableWebSecurity
+	static class CorsFilterInLambdaConfig extends WebSecurityConfigurerAdapter {
+
+		@Override
+		protected void configure(HttpSecurity http) throws Exception {
+			// @formatter:off
+			http
+				.authorizeRequests()
+					.anyRequest().authenticated()
+					.and()
+				.cors(withDefaults());
+			// @formatter:on
+		}
+
+		@Bean
+		CorsFilter corsFilter() {
+			UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
+			CorsConfiguration corsConfiguration = new CorsConfiguration();
+			corsConfiguration.setAllowedOrigins(Collections.singletonList("*"));
+			corsConfiguration.setAllowedMethods(Arrays.asList(
+					RequestMethod.GET.name(),
+					RequestMethod.POST.name()));
+			source.registerCorsConfiguration("/**", corsConfiguration);
+			return new CorsFilter(source);
+		}
+	}
 }