mirror of
https://github.com/spring-projects/spring-security.git
synced 2025-05-30 00:32:14 +00:00
Automatically enable .cors() if CorsConfigurationSource bean is present
Closes gh-5011
This commit is contained in:
parent
52e12ad64b
commit
618847418f
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* Copyright 2002-2023 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.
|
||||
@ -47,6 +47,7 @@ import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
import org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter;
|
||||
import org.springframework.web.accept.ContentNegotiationStrategy;
|
||||
import org.springframework.web.accept.HeaderContentNegotiationStrategy;
|
||||
import org.springframework.web.cors.CorsConfigurationSource;
|
||||
|
||||
import static org.springframework.security.config.Customizer.withDefaults;
|
||||
|
||||
@ -124,10 +125,18 @@ class HttpSecurityConfiguration {
|
||||
.apply(new DefaultLoginPageConfigurer<>());
|
||||
http.logout(withDefaults());
|
||||
// @formatter:on
|
||||
applyCorsIfAvailable(http);
|
||||
applyDefaultConfigurers(http);
|
||||
return http;
|
||||
}
|
||||
|
||||
private void applyCorsIfAvailable(HttpSecurity http) throws Exception {
|
||||
String[] beanNames = this.context.getBeanNamesForType(CorsConfigurationSource.class);
|
||||
if (beanNames.length == 1) {
|
||||
http.cors(withDefaults());
|
||||
}
|
||||
}
|
||||
|
||||
private AuthenticationManager authenticationManager() throws Exception {
|
||||
return this.authenticationConfiguration.getAuthenticationManager();
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* Copyright 2002-2023 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.
|
||||
@ -66,6 +66,7 @@ import org.springframework.security.web.SecurityFilterChain;
|
||||
import org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter;
|
||||
import org.springframework.security.web.authentication.ui.DefaultLogoutPageGeneratingFilter;
|
||||
import org.springframework.security.web.header.writers.frameoptions.XFrameOptionsHeaderWriter;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
import org.springframework.test.web.servlet.MvcResult;
|
||||
import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;
|
||||
@ -73,6 +74,10 @@ import org.springframework.web.accept.ContentNegotiationStrategy;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.context.request.NativeWebRequest;
|
||||
import org.springframework.web.cors.CorsConfiguration;
|
||||
import org.springframework.web.cors.CorsConfigurationSource;
|
||||
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
|
||||
import org.springframework.web.filter.CorsFilter;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
@ -350,6 +355,16 @@ public class HttpSecurityConfigurationTests {
|
||||
DefaultLogoutPageGeneratingFilter.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void configureWhenCorsConfigurationSourceThenApplyCors() {
|
||||
this.spring.register(CorsConfigurationSourceConfig.class, DefaultWithFilterChainConfig.class).autowire();
|
||||
SecurityFilterChain filterChain = this.spring.getContext().getBean(SecurityFilterChain.class);
|
||||
CorsFilter corsFilter = (CorsFilter) filterChain.getFilters().stream().filter((f) -> f instanceof CorsFilter)
|
||||
.findFirst().get();
|
||||
Object configSource = ReflectionTestUtils.getField(corsFilter, "configSource");
|
||||
assertThat(configSource).isInstanceOf(UrlBasedCorsConfigurationSource.class);
|
||||
}
|
||||
|
||||
@RestController
|
||||
static class NameController {
|
||||
|
||||
@ -614,6 +629,20 @@ public class HttpSecurityConfigurationTests {
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class CorsConfigurationSourceConfig {
|
||||
|
||||
@Bean
|
||||
CorsConfigurationSource corsConfigurationSource() {
|
||||
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
|
||||
CorsConfiguration corsConfiguration = new CorsConfiguration();
|
||||
corsConfiguration.setAllowedOrigins(List.of("http://localhost:8080"));
|
||||
source.registerCorsConfiguration("/**", corsConfiguration);
|
||||
return source;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class DefaultConfigurer extends AbstractHttpConfigurer<DefaultConfigurer, HttpSecurity> {
|
||||
|
||||
boolean init;
|
||||
|
@ -6,7 +6,8 @@ CORS must be processed before Spring Security, because the pre-flight request do
|
||||
If the request does not contain any cookies and Spring Security is first, the request determines that the user is not authenticated (since there are no cookies in the request) and rejects it.
|
||||
|
||||
The easiest way to ensure that CORS is handled first is to use the `CorsFilter`.
|
||||
Users can integrate the `CorsFilter` with Spring Security by providing a `CorsConfigurationSource` that uses the following:
|
||||
Users can integrate the `CorsFilter` with Spring Security by providing a `CorsConfigurationSource`.
|
||||
For example, the following will integrate CORS support within Spring Security:
|
||||
|
||||
[tabs]
|
||||
======
|
||||
@ -14,28 +15,14 @@ Java::
|
||||
+
|
||||
[source,java,role="primary"]
|
||||
----
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
public class WebSecurityConfig {
|
||||
|
||||
@Bean
|
||||
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
|
||||
http
|
||||
// by default uses a Bean by the name of corsConfigurationSource
|
||||
.cors(withDefaults())
|
||||
...
|
||||
return http.build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
CorsConfigurationSource corsConfigurationSource() {
|
||||
CorsConfiguration configuration = new CorsConfiguration();
|
||||
configuration.setAllowedOrigins(Arrays.asList("https://example.com"));
|
||||
configuration.setAllowedMethods(Arrays.asList("GET","POST"));
|
||||
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
|
||||
source.registerCorsConfiguration("/**", configuration);
|
||||
return source;
|
||||
}
|
||||
@Bean
|
||||
CorsConfigurationSource corsConfigurationSource() {
|
||||
CorsConfiguration configuration = new CorsConfiguration();
|
||||
configuration.setAllowedOrigins(Arrays.asList("https://example.com"));
|
||||
configuration.setAllowedMethods(Arrays.asList("GET","POST"));
|
||||
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
|
||||
source.registerCorsConfiguration("/**", configuration);
|
||||
return source;
|
||||
}
|
||||
----
|
||||
|
||||
@ -43,28 +30,14 @@ Kotlin::
|
||||
+
|
||||
[source,kotlin,role="secondary"]
|
||||
----
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
open class WebSecurityConfig {
|
||||
@Bean
|
||||
open fun filterChain(http: HttpSecurity): SecurityFilterChain {
|
||||
http {
|
||||
// by default uses a Bean by the name of corsConfigurationSource
|
||||
cors { }
|
||||
// ...
|
||||
}
|
||||
return http.build()
|
||||
}
|
||||
|
||||
@Bean
|
||||
open fun corsConfigurationSource(): CorsConfigurationSource {
|
||||
val configuration = CorsConfiguration()
|
||||
configuration.allowedOrigins = listOf("https://example.com")
|
||||
configuration.allowedMethods = listOf("GET", "POST")
|
||||
val source = UrlBasedCorsConfigurationSource()
|
||||
source.registerCorsConfiguration("/**", configuration)
|
||||
return source
|
||||
}
|
||||
@Bean
|
||||
fun corsConfigurationSource(): CorsConfigurationSource {
|
||||
val configuration = CorsConfiguration()
|
||||
configuration.allowedOrigins = listOf("https://example.com")
|
||||
configuration.allowedMethods = listOf("GET", "POST")
|
||||
val source = UrlBasedCorsConfigurationSource()
|
||||
source.registerCorsConfiguration("/**", configuration)
|
||||
return source
|
||||
}
|
||||
----
|
||||
======
|
||||
@ -137,3 +110,76 @@ The following listing does the same thing in XML:
|
||||
...
|
||||
</http>
|
||||
----
|
||||
|
||||
If you have more than one `CorsConfigurationSource` bean, Spring Security won't automatically configure CORS support for you, that is because it cannot decide which one to use.
|
||||
If you want to specify different `CorsConfigurationSource` for each `SecurityFilterChain`, you can pass it directly into the `.cors()` DSL.
|
||||
|
||||
[tabs]
|
||||
======
|
||||
Java::
|
||||
+
|
||||
[source,java,role="primary"]
|
||||
----
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
public class WebSecurityConfig {
|
||||
|
||||
@Bean
|
||||
@Order(0)
|
||||
public SecurityFilterChain apiFilterChain(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.securityMatcher("/api/**")
|
||||
.cors((cors) -> cors
|
||||
.configurationSource(apiConfigurationSource())
|
||||
)
|
||||
...
|
||||
return http.build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Order(1)
|
||||
public SecurityFilterChain myOtherFilterChain(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.cors((cors) -> cors
|
||||
.configurationSource(myWebsiteConfigurationSource())
|
||||
)
|
||||
...
|
||||
return http.build();
|
||||
}
|
||||
|
||||
CorsConfigurationSource apiConfigurationSource() {
|
||||
CorsConfiguration configuration = new CorsConfiguration();
|
||||
configuration.setAllowedOrigins(Arrays.asList("https://api.example.com"));
|
||||
configuration.setAllowedMethods(Arrays.asList("GET","POST"));
|
||||
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
|
||||
source.registerCorsConfiguration("/**", configuration);
|
||||
return source;
|
||||
}
|
||||
|
||||
CorsConfigurationSource myWebsiteConfigurationSource() {
|
||||
CorsConfiguration configuration = new CorsConfiguration();
|
||||
configuration.setAllowedOrigins(Arrays.asList("https://example.com"));
|
||||
configuration.setAllowedMethods(Arrays.asList("GET","POST"));
|
||||
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
|
||||
source.registerCorsConfiguration("/**", configuration);
|
||||
return source;
|
||||
}
|
||||
|
||||
}
|
||||
----
|
||||
|
||||
Kotlin::
|
||||
+
|
||||
[source,kotlin,role="secondary"]
|
||||
----
|
||||
@Bean
|
||||
fun corsConfigurationSource(): CorsConfigurationSource {
|
||||
val configuration = CorsConfiguration()
|
||||
configuration.allowedOrigins = listOf("https://example.com")
|
||||
configuration.allowedMethods = listOf("GET", "POST")
|
||||
val source = UrlBasedCorsConfigurationSource()
|
||||
source.registerCorsConfiguration("/**", configuration)
|
||||
return source
|
||||
}
|
||||
----
|
||||
======
|
||||
|
@ -4,3 +4,6 @@
|
||||
Spring Security 6.2 provides a number of new features.
|
||||
Below are the highlights of the release.
|
||||
|
||||
== Configuration
|
||||
|
||||
* https://github.com/spring-projects/spring-security/issues/5011[gh-5011] - xref:servlet/integrations/cors.adoc[(docs)] Automatically enable `.cors()` if `CorsConfigurationSource` bean is present
|
||||
|
Loading…
x
Reference in New Issue
Block a user