Skip to content

Commit 4199240

Browse files
committed
Add Support for PreFlightRequestFilter
Closes gh-18926
1 parent 0ef8a4f commit 4199240

8 files changed

Lines changed: 538 additions & 24 deletions

File tree

config/src/main/java/org/springframework/security/config/annotation/web/configurers/CorsConfigurer.java

Lines changed: 78 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -21,15 +21,18 @@
2121
import org.springframework.security.config.Customizer;
2222
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
2323
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
24-
import org.springframework.util.Assert;
25-
import org.springframework.web.cors.CorsConfiguration;
2624
import org.springframework.web.cors.CorsConfigurationSource;
25+
import org.springframework.web.cors.PreFlightRequestHandler;
2726
import org.springframework.web.filter.CorsFilter;
27+
import org.springframework.web.filter.PreFlightRequestFilter;
2828

2929
/**
30-
* Adds {@link CorsFilter} to the Spring Security filter chain. If a bean by the name of
31-
* corsFilter is provided, that {@link CorsFilter} is used. Else if
32-
* corsConfigurationSource is defined, then that {@link CorsConfiguration} is used.
30+
* Adds {@link CorsFilter} or {@link PreFlightRequestFilter} to the Spring Security filter
31+
* chain. If a bean by the name of corsFilter is provided, that {@link CorsFilter} is
32+
* used. Else if corsConfigurationSource is defined, then that
33+
* {@link CorsConfigurationSource} is used. If a {@link PreFlightRequestHandler} is set on
34+
* this configurer, {@link CorsFilter} is not used and {@link PreFlightRequestFilter} is
35+
* registered instead.
3336
*
3437
* @param <H> the builder to return.
3538
* @author Rob Winch
@@ -43,6 +46,8 @@ public class CorsConfigurer<H extends HttpSecurityBuilder<H>> extends AbstractHt
4346

4447
private CorsConfigurationSource configurationSource;
4548

49+
private PreFlightRequestHandler preFlightRequestHandler;
50+
4651
/**
4752
* Creates a new instance
4853
*
@@ -56,30 +61,85 @@ public CorsConfigurer<H> configurationSource(CorsConfigurationSource configurati
5661
return this;
5762
}
5863

64+
/**
65+
* Use the given {@link PreFlightRequestHandler} for CORS preflight requests. When
66+
* set, {@link CorsFilter} is not used. Cannot be combined with
67+
* {@link #configurationSource(CorsConfigurationSource)}.
68+
* @param preFlightRequestHandler the handler to use
69+
* @return the {@link CorsConfigurer} for additional configuration
70+
*/
71+
public CorsConfigurer<H> preFlightRequestHandler(PreFlightRequestHandler preFlightRequestHandler) {
72+
this.preFlightRequestHandler = preFlightRequestHandler;
73+
return this;
74+
}
75+
5976
@Override
6077
public void configure(H http) {
6178
ApplicationContext context = http.getSharedObject(ApplicationContext.class);
79+
80+
if (this.configurationSource != null && this.preFlightRequestHandler != null) {
81+
throw new IllegalStateException(
82+
"Cannot configure both a CorsConfigurationSource and a PreFlightRequestHandler on CorsConfigurer");
83+
}
84+
6285
CorsFilter corsFilter = getCorsFilter(context);
63-
Assert.state(corsFilter != null, () -> "Please configure either a " + CORS_FILTER_BEAN_NAME + " bean or a "
64-
+ CORS_CONFIGURATION_SOURCE_BEAN_NAME + "bean.");
65-
http.addFilter(corsFilter);
86+
if (corsFilter != null) {
87+
http.addFilter(corsFilter);
88+
return;
89+
}
90+
PreFlightRequestHandler preFlightRequestHandlerBean = getPreFlightRequestHandler(context);
91+
if (preFlightRequestHandlerBean != null) {
92+
http.addFilterBefore(new PreFlightRequestFilter(preFlightRequestHandlerBean), CorsFilter.class);
93+
return;
94+
}
95+
throw new NoSuchBeanDefinitionException(CorsConfigurationSource.class,
96+
"Failed to find a bean that implements `CorsConfigurationSource`. Please ensure that you are using "
97+
+ "`@EnableWebMvc`, are publishing a `WebMvcConfigurer`, or are publishing a `CorsConfigurationSource` bean.");
98+
}
99+
100+
private PreFlightRequestHandler getPreFlightRequestHandler(ApplicationContext context) {
101+
if (this.configurationSource != null) {
102+
return null;
103+
}
104+
if (this.preFlightRequestHandler != null) {
105+
return this.preFlightRequestHandler;
106+
}
107+
if (context == null) {
108+
return null;
109+
}
110+
if (context.getBeanNamesForType(PreFlightRequestHandler.class).length > 0) {
111+
return context.getBean(PreFlightRequestHandler.class);
112+
}
113+
return null;
114+
}
115+
116+
private CorsConfigurationSource getCorsConfigurationSource(ApplicationContext context) {
117+
if (context == null) {
118+
return null;
119+
}
120+
boolean containsCorsSource = context.containsBeanDefinition(CORS_CONFIGURATION_SOURCE_BEAN_NAME);
121+
if (containsCorsSource) {
122+
return context.getBean(CORS_CONFIGURATION_SOURCE_BEAN_NAME, CorsConfigurationSource.class);
123+
}
124+
return MvcCorsFilter.getMvcCorsConfigurationSource(context);
66125
}
67126

68127
private CorsFilter getCorsFilter(ApplicationContext context) {
128+
if (this.preFlightRequestHandler != null) {
129+
return null;
130+
}
69131
if (this.configurationSource != null) {
70132
return new CorsFilter(this.configurationSource);
71133
}
72-
boolean containsCorsFilter = context.containsBeanDefinition(CORS_FILTER_BEAN_NAME);
134+
boolean containsCorsFilter = context != null && context.containsBeanDefinition(CORS_FILTER_BEAN_NAME);
73135
if (containsCorsFilter) {
74136
return context.getBean(CORS_FILTER_BEAN_NAME, CorsFilter.class);
75137
}
76-
boolean containsCorsSource = context.containsBean(CORS_CONFIGURATION_SOURCE_BEAN_NAME);
77-
if (containsCorsSource) {
78-
CorsConfigurationSource configurationSource = context.getBean(CORS_CONFIGURATION_SOURCE_BEAN_NAME,
79-
CorsConfigurationSource.class);
80-
return new CorsFilter(configurationSource);
138+
CorsConfigurationSource corsConfigurationSource = getCorsConfigurationSource(context);
139+
if (corsConfigurationSource != null) {
140+
return new CorsFilter(corsConfigurationSource);
81141
}
82-
return MvcCorsFilter.getMvcCorsFilter(context);
142+
return null;
83143
}
84144

85145
static class MvcCorsFilter {
@@ -92,15 +152,11 @@ static class MvcCorsFilter {
92152
* @param context
93153
* @return
94154
*/
95-
private static CorsFilter getMvcCorsFilter(ApplicationContext context) {
155+
private static CorsConfigurationSource getMvcCorsConfigurationSource(ApplicationContext context) {
96156
if (context.containsBean(HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME)) {
97-
CorsConfigurationSource corsConfigurationSource = context
98-
.getBean(HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME, CorsConfigurationSource.class);
99-
return new CorsFilter(corsConfigurationSource);
157+
return context.getBean(HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME, CorsConfigurationSource.class);
100158
}
101-
throw new NoSuchBeanDefinitionException(CorsConfigurationSource.class,
102-
"Failed to find a bean that implements `CorsConfigurationSource`. Please ensure that you are using "
103-
+ "`@EnableWebMvc`, are publishing a `WebMvcConfigurer`, or are publishing a `CorsConfigurationSource` bean.");
159+
return null;
104160
}
105161

106162
}

config/src/main/kotlin/org/springframework/security/config/annotation/web/CorsDsl.kt

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,18 +19,22 @@ package org.springframework.security.config.annotation.web
1919
import org.springframework.security.config.annotation.web.builders.HttpSecurity
2020
import org.springframework.security.config.annotation.web.configurers.CorsConfigurer
2121
import org.springframework.web.cors.CorsConfigurationSource
22+
import org.springframework.web.cors.PreFlightRequestHandler
2223

2324
/**
2425
* A Kotlin DSL to configure [HttpSecurity] CORS using idiomatic Kotlin code.
2526
*
2627
* @author Eleftheria Stein
2728
* @since 5.3
2829
* @property configurationSource the [CorsConfigurationSource] to use.
30+
* @property preFlightRequestHandler the [PreFlightRequestHandler] to use instead of [CorsFilter].
2931
*/
3032
@SecurityMarker
3133
class CorsDsl {
3234
var configurationSource: CorsConfigurationSource? = null
3335

36+
var preFlightRequestHandler: PreFlightRequestHandler? = null
37+
3438
private var disabled = false
3539

3640
/**
@@ -42,7 +46,8 @@ class CorsDsl {
4246

4347
internal fun get(): (CorsConfigurer<HttpSecurity>) -> Unit {
4448
return { cors ->
45-
configurationSource?.also { cors.configurationSource(configurationSource) }
49+
configurationSource?.also { cors.configurationSource(it) }
50+
preFlightRequestHandler?.also { cors.preFlightRequestHandler(it) }
4651
if (disabled) {
4752
cors.disable()
4853
}

0 commit comments

Comments
 (0)