Add Support for PreFlightRequestFilter

Closes gh-18926
This commit is contained in:
Robert Winch 2026-03-19 14:56:46 -05:00 committed by Rob Winch
parent 0ef8a4ff27
commit 4199240662
8 changed files with 538 additions and 24 deletions

View File

@ -21,15 +21,18 @@ import org.springframework.context.ApplicationContext;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.util.Assert;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.PreFlightRequestHandler;
import org.springframework.web.filter.CorsFilter;
import org.springframework.web.filter.PreFlightRequestFilter;
/**
* Adds {@link CorsFilter} to the Spring Security filter chain. 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.
* Adds {@link CorsFilter} or {@link PreFlightRequestFilter} to the Spring Security filter
* chain. If a bean by the name of corsFilter is provided, that {@link CorsFilter} is
* used. Else if corsConfigurationSource is defined, then that
* {@link CorsConfigurationSource} is used. If a {@link PreFlightRequestHandler} is set on
* this configurer, {@link CorsFilter} is not used and {@link PreFlightRequestFilter} is
* registered instead.
*
* @param <H> the builder to return.
* @author Rob Winch
@ -43,6 +46,8 @@ public class CorsConfigurer<H extends HttpSecurityBuilder<H>> extends AbstractHt
private CorsConfigurationSource configurationSource;
private PreFlightRequestHandler preFlightRequestHandler;
/**
* Creates a new instance
*
@ -56,30 +61,85 @@ public class CorsConfigurer<H extends HttpSecurityBuilder<H>> extends AbstractHt
return this;
}
/**
* Use the given {@link PreFlightRequestHandler} for CORS preflight requests. When
* set, {@link CorsFilter} is not used. Cannot be combined with
* {@link #configurationSource(CorsConfigurationSource)}.
* @param preFlightRequestHandler the handler to use
* @return the {@link CorsConfigurer} for additional configuration
*/
public CorsConfigurer<H> preFlightRequestHandler(PreFlightRequestHandler preFlightRequestHandler) {
this.preFlightRequestHandler = preFlightRequestHandler;
return this;
}
@Override
public void configure(H http) {
ApplicationContext context = http.getSharedObject(ApplicationContext.class);
if (this.configurationSource != null && this.preFlightRequestHandler != null) {
throw new IllegalStateException(
"Cannot configure both a CorsConfigurationSource and a PreFlightRequestHandler on CorsConfigurer");
}
CorsFilter corsFilter = getCorsFilter(context);
Assert.state(corsFilter != null, () -> "Please configure either a " + CORS_FILTER_BEAN_NAME + " bean or a "
+ CORS_CONFIGURATION_SOURCE_BEAN_NAME + "bean.");
http.addFilter(corsFilter);
if (corsFilter != null) {
http.addFilter(corsFilter);
return;
}
PreFlightRequestHandler preFlightRequestHandlerBean = getPreFlightRequestHandler(context);
if (preFlightRequestHandlerBean != null) {
http.addFilterBefore(new PreFlightRequestFilter(preFlightRequestHandlerBean), CorsFilter.class);
return;
}
throw new NoSuchBeanDefinitionException(CorsConfigurationSource.class,
"Failed to find a bean that implements `CorsConfigurationSource`. Please ensure that you are using "
+ "`@EnableWebMvc`, are publishing a `WebMvcConfigurer`, or are publishing a `CorsConfigurationSource` bean.");
}
private PreFlightRequestHandler getPreFlightRequestHandler(ApplicationContext context) {
if (this.configurationSource != null) {
return null;
}
if (this.preFlightRequestHandler != null) {
return this.preFlightRequestHandler;
}
if (context == null) {
return null;
}
if (context.getBeanNamesForType(PreFlightRequestHandler.class).length > 0) {
return context.getBean(PreFlightRequestHandler.class);
}
return null;
}
private CorsConfigurationSource getCorsConfigurationSource(ApplicationContext context) {
if (context == null) {
return null;
}
boolean containsCorsSource = context.containsBeanDefinition(CORS_CONFIGURATION_SOURCE_BEAN_NAME);
if (containsCorsSource) {
return context.getBean(CORS_CONFIGURATION_SOURCE_BEAN_NAME, CorsConfigurationSource.class);
}
return MvcCorsFilter.getMvcCorsConfigurationSource(context);
}
private CorsFilter getCorsFilter(ApplicationContext context) {
if (this.preFlightRequestHandler != null) {
return null;
}
if (this.configurationSource != null) {
return new CorsFilter(this.configurationSource);
}
boolean containsCorsFilter = context.containsBeanDefinition(CORS_FILTER_BEAN_NAME);
boolean containsCorsFilter = context != null && context.containsBeanDefinition(CORS_FILTER_BEAN_NAME);
if (containsCorsFilter) {
return context.getBean(CORS_FILTER_BEAN_NAME, CorsFilter.class);
}
boolean containsCorsSource = context.containsBean(CORS_CONFIGURATION_SOURCE_BEAN_NAME);
if (containsCorsSource) {
CorsConfigurationSource configurationSource = context.getBean(CORS_CONFIGURATION_SOURCE_BEAN_NAME,
CorsConfigurationSource.class);
return new CorsFilter(configurationSource);
CorsConfigurationSource corsConfigurationSource = getCorsConfigurationSource(context);
if (corsConfigurationSource != null) {
return new CorsFilter(corsConfigurationSource);
}
return MvcCorsFilter.getMvcCorsFilter(context);
return null;
}
static class MvcCorsFilter {
@ -92,15 +152,11 @@ public class CorsConfigurer<H extends HttpSecurityBuilder<H>> extends AbstractHt
* @param context
* @return
*/
private static CorsFilter getMvcCorsFilter(ApplicationContext context) {
private static CorsConfigurationSource getMvcCorsConfigurationSource(ApplicationContext context) {
if (context.containsBean(HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME)) {
CorsConfigurationSource corsConfigurationSource = context
.getBean(HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME, CorsConfigurationSource.class);
return new CorsFilter(corsConfigurationSource);
return context.getBean(HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME, CorsConfigurationSource.class);
}
throw new NoSuchBeanDefinitionException(CorsConfigurationSource.class,
"Failed to find a bean that implements `CorsConfigurationSource`. Please ensure that you are using "
+ "`@EnableWebMvc`, are publishing a `WebMvcConfigurer`, or are publishing a `CorsConfigurationSource` bean.");
return null;
}
}

View File

@ -19,6 +19,7 @@ package org.springframework.security.config.annotation.web
import org.springframework.security.config.annotation.web.builders.HttpSecurity
import org.springframework.security.config.annotation.web.configurers.CorsConfigurer
import org.springframework.web.cors.CorsConfigurationSource
import org.springframework.web.cors.PreFlightRequestHandler
/**
* A Kotlin DSL to configure [HttpSecurity] CORS using idiomatic Kotlin code.
@ -26,11 +27,14 @@ import org.springframework.web.cors.CorsConfigurationSource
* @author Eleftheria Stein
* @since 5.3
* @property configurationSource the [CorsConfigurationSource] to use.
* @property preFlightRequestHandler the [PreFlightRequestHandler] to use instead of [CorsFilter].
*/
@SecurityMarker
class CorsDsl {
var configurationSource: CorsConfigurationSource? = null
var preFlightRequestHandler: PreFlightRequestHandler? = null
private var disabled = false
/**
@ -42,7 +46,8 @@ class CorsDsl {
internal fun get(): (CorsConfigurer<HttpSecurity>) -> Unit {
return { cors ->
configurationSource?.also { cors.configurationSource(configurationSource) }
configurationSource?.also { cors.configurationSource(it) }
preFlightRequestHandler?.also { cors.preFlightRequestHandler(it) }
if (disabled) {
cors.disable()
}

View File

@ -40,6 +40,7 @@ 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.CorsConfigurationSource;
import org.springframework.web.cors.PreFlightRequestHandler;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
@ -196,6 +197,73 @@ public class CorsConfigurerTests {
.andExpect(header().exists("X-Content-Type-Options"));
}
@Test
public void optionsWhenPreFlightRequestHandlerBeanThenHandled() throws Exception {
this.spring.register(PreFlightRequestHandlerConfig.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("X-Pre-Flight"));
}
@Test
public void optionsWhenNoPreFlightRequestHandlerBeanThenCorsFilterUsed() throws Exception {
this.spring.register(NoPreFlightRequestHandlerConfig.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().doesNotExist("X-Pre-Flight"));
}
@Test
public void optionsWhenExplicitConfigurationSourceThenPreFlightRequestHandlerBeanIgnored() throws Exception {
this.spring.register(ExplicitConfigurationSourceWithPreFlightRequestHandlerBeanConfig.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().doesNotExist("X-Pre-Flight"));
}
@Test
public void optionsWhenPreFlightRequestHandlerMemberThenHandled() throws Exception {
this.spring.register(PreFlightRequestHandlerMemberConfig.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("X-Pre-Flight"));
}
@Test
public void configureWhenBothConfigurationSourceAndPreFlightRequestHandlerMemberThenIllegalState() {
assertThatExceptionOfType(BeanCreationException.class)
.isThrownBy(() -> this.spring.register(BothCorsConfigurerMembersConfig.class).autowire())
.havingRootCause()
.isInstanceOf(IllegalStateException.class)
.withMessageContaining("Cannot configure both");
}
@Test
public void optionsWhenPreFlightRequestHandlerMemberThenCorsFilterBeanIgnored() throws Exception {
this.spring.register(PreFlightRequestHandlerMemberWithCorsFilterBeanConfig.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("X-Pre-Flight"))
.andExpect(header().doesNotExist("Access-Control-Allow-Origin"));
}
@Configuration
@EnableWebSecurity
static class DefaultCorsConfig {
@ -382,4 +450,150 @@ public class CorsConfigurerTests {
}
@Configuration
@EnableWebSecurity
static class PreFlightRequestHandlerConfig {
@Bean
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
// @formatter:off
http
.authorizeHttpRequests((requests) -> requests
.anyRequest().authenticated())
.cors(withDefaults());
return http.build();
// @formatter:on
}
@Bean
PreFlightRequestHandler preFlightRequestHandler() {
return (request, response) -> response.addHeader("X-Pre-Flight", "Handled");
}
}
@Configuration
@EnableWebSecurity
static class NoPreFlightRequestHandlerConfig {
@Bean
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
// @formatter:off
http
.authorizeHttpRequests((requests) -> requests
.anyRequest().authenticated())
.cors(withDefaults());
return http.build();
// @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;
}
}
@Configuration
@EnableWebSecurity
static class ExplicitConfigurationSourceWithPreFlightRequestHandlerBeanConfig {
@Bean
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
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);
// @formatter:off
http
.authorizeHttpRequests((requests) -> requests
.anyRequest().authenticated())
.cors((cors) -> cors.configurationSource(source));
return http.build();
// @formatter:on
}
@Bean
PreFlightRequestHandler preFlightRequestHandler() {
return (request, response) -> response.addHeader("X-Pre-Flight", "Handled");
}
}
@Configuration
@EnableWebSecurity
static class PreFlightRequestHandlerMemberConfig {
@Bean
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
// @formatter:off
http
.authorizeHttpRequests((requests) -> requests
.anyRequest().authenticated())
.cors((cors) -> cors.preFlightRequestHandler(
(request, response) -> response.addHeader("X-Pre-Flight", "Member")));
return http.build();
// @formatter:on
}
}
@Configuration
@EnableWebSecurity
static class BothCorsConfigurerMembersConfig {
@Bean
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
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);
// @formatter:off
http
.authorizeHttpRequests((requests) -> requests
.anyRequest().authenticated())
.cors((cors) -> cors
.configurationSource(source)
.preFlightRequestHandler((request, response) -> response.addHeader("X-Pre-Flight", "Handled")));
return http.build();
// @formatter:on
}
}
@Configuration
@EnableWebSecurity
static class PreFlightRequestHandlerMemberWithCorsFilterBeanConfig {
@Bean
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
// @formatter:off
http
.authorizeHttpRequests((requests) -> requests
.anyRequest().authenticated())
.cors((cors) -> cors.preFlightRequestHandler(
(request, response) -> response.addHeader("X-Pre-Flight", "Member")));
return http.build();
// @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);
}
}
}

View File

@ -16,7 +16,10 @@
package org.springframework.security.config.annotation.web
import org.assertj.core.api.Assertions.assertThat
import org.assertj.core.api.Assertions.assertThatThrownBy
import org.assertj.core.api.Assertions.catchThrowable
import org.assertj.core.util.Throwables
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.ExtendWith
import org.springframework.beans.factory.BeanCreationException
@ -35,9 +38,14 @@ import org.springframework.test.web.servlet.get
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.RequestMethod
import org.springframework.web.bind.annotation.RestController
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders.options
import org.springframework.test.web.servlet.result.MockMvcResultMatchers.header
import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status
import org.springframework.web.cors.CorsConfiguration
import org.springframework.web.cors.CorsConfigurationSource
import org.springframework.web.cors.PreFlightRequestHandler
import org.springframework.web.cors.UrlBasedCorsConfigurationSource
import org.springframework.web.filter.CorsFilter
import org.springframework.web.servlet.config.annotation.EnableWebMvc
/**
@ -153,7 +161,7 @@ class CorsDslTests {
@Test
fun `CORS when CORS configuration source dsl then responds with CORS header`() {
this.spring.register(CorsCrossOriginBeanConfig::class.java, HomeController::class.java).autowire()
this.spring.register(CorsCrossOriginSourceConfig::class.java, HomeController::class.java).autowire()
this.mockMvc.get("/")
{
@ -185,6 +193,117 @@ class CorsDslTests {
}
}
@Test
fun `CORS when preFlight request handler dsl then OPTIONS uses handler`() {
this.spring.register(PreFlightRequestHandlerDslConfig::class.java).autowire()
this.mockMvc.perform(options("/")
.header(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, RequestMethod.POST.name)
.header(HttpHeaders.ORIGIN, "https://example.com"))
.andExpect(status().isOk)
.andExpect(header().exists("X-Pre-Flight"))
}
@Configuration
@EnableWebSecurity
open class PreFlightRequestHandlerDslConfig {
@Bean
open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
http {
authorizeHttpRequests {
authorize(anyRequest, authenticated)
}
cors {
preFlightRequestHandler = PreFlightRequestHandler { _, response ->
response.addHeader("X-Pre-Flight", "Dsl")
}
}
}
return http.build()
}
}
@Test
fun `CORS when configuration source and preFlight handler dsl then illegal state`() {
val thrown = catchThrowable {
this.spring.register(BothCorsDslMembersConfig::class.java).autowire()
}
assertThat(thrown).isInstanceOf(BeanCreationException::class.java)
assertThat(Throwables.getRootCause(thrown))
.isInstanceOf(IllegalStateException::class.java)
.hasMessageContaining("Cannot configure both")
}
@Configuration
@EnableWebSecurity
open class BothCorsDslMembersConfig {
@Bean
open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
val source = UrlBasedCorsConfigurationSource()
val corsConfiguration = CorsConfiguration()
corsConfiguration.allowedOrigins = listOf("*")
corsConfiguration.allowedMethods = listOf(
RequestMethod.GET.name,
RequestMethod.POST.name)
source.registerCorsConfiguration("/**", corsConfiguration)
http {
authorizeHttpRequests {
authorize(anyRequest, authenticated)
}
cors {
configurationSource = source
preFlightRequestHandler = PreFlightRequestHandler { _, response ->
response.addHeader("X-Pre-Flight", "Dsl")
}
}
}
return http.build()
}
}
@Test
fun `CORS when preFlight handler dsl then CorsFilter bean ignored on OPTIONS`() {
this.spring.register(PreFlightRequestHandlerDslWithCorsFilterBeanConfig::class.java).autowire()
this.mockMvc.perform(options("/")
.header(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, RequestMethod.POST.name)
.header(HttpHeaders.ORIGIN, "https://example.com"))
.andExpect(status().isOk)
.andExpect(header().exists("X-Pre-Flight"))
.andExpect(header().doesNotExist("Access-Control-Allow-Origin"))
}
@Configuration
@EnableWebSecurity
open class PreFlightRequestHandlerDslWithCorsFilterBeanConfig {
@Bean
open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
http {
authorizeHttpRequests {
authorize(anyRequest, authenticated)
}
cors {
preFlightRequestHandler = PreFlightRequestHandler { _, response ->
response.addHeader("X-Pre-Flight", "Dsl")
}
}
}
return http.build()
}
@Bean
open fun corsFilter(): CorsFilter {
val source = UrlBasedCorsConfigurationSource()
val corsConfiguration = CorsConfiguration()
corsConfiguration.allowedOrigins = listOf("*")
corsConfiguration.allowedMethods = listOf(
RequestMethod.GET.name,
RequestMethod.POST.name)
source.registerCorsConfiguration("/**", corsConfiguration)
return CorsFilter(source)
}
}
@RestController
private class HomeController {
@GetMapping("/")

View File

@ -184,6 +184,23 @@ fun corsConfigurationSource(): UrlBasedCorsConfigurationSource {
----
======
[[cors-preflight-request-handler]]
== `PreFlightRequestHandler` and `PreFlightRequestFilter`
Spring Framework defines {spring-framework-api-url}org/springframework/web/cors/PreFlightRequestHandler.html[`PreFlightRequestHandler`] for applications that need to handle CORS preflight (`OPTIONS`) requests outside of `CorsFilter`.
When Spring Security selects a `PreFlightRequestHandler` for a filter chain, it registers {spring-framework-api-url}org/springframework/web/filter/PreFlightRequestFilter.html[`PreFlightRequestFilter`] in the security filter chain (before `CorsFilter`) so preflight can be handled early in the request lifecycle.
You can supply a handler in either of these ways:
* Pass a handler directly with the `preFlightRequestHandler` attribute.
* Register a `PreFlightRequestHandler` bean when cors is enabled and when no `CorsConfigurationSource` or `CorsFilter` is chosen for that chain.
You must not configure both `configurationSource` and `preFlightRequestHandler` on the same `CorsConfigurer`; doing so results in an error at startup.
The following example explicitly registers a `PreFlightRequestHandler` using the `preFlightRequestHandler`:
include-code::./CorsPreFlightRequestHandlerExample[tag=preflightRequestHandler,indent=0]
[WARNING]
====
CORS is a browser-based security feature.

View File

@ -8,6 +8,7 @@
* Added xref:servlet/authorization/architecture.adoc#authz-conditional-authorization-manager[ConditionalAuthorizationManager]
* Added `when` and `withWhen` conditions to `AuthorizationManagerFactories.multiFactor()` for xref:servlet/authentication/mfa.adoc#programmatic-mfa[Programmatic MFA]
* Added `MultiFactorCondition.WEBAUTHN_REGISTERED` to `@EnableMultiFactorAuthentication(when = ...)` for xref:servlet/authentication/mfa.adoc#mfa-when-webauthn-registered[conditionally requiring MFA for WebAuthn Users]
* https://github.com/spring-projects/spring-security/issues/18926[gh-18926] - xref:servlet/integrations/cors.adoc[Add `PreFlightRequestFilter` Support]
== OAuth 2.0

View File

@ -0,0 +1,49 @@
/*
* Copyright 2004-present 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
*
* https://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.docs.servlet.integrations.corspreflightrequesthandler;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.web.cors.PreFlightRequestHandler;
@Configuration
@EnableWebSecurity
class CorsPreFlightRequestHandlerExample {
@Bean
PreFlightRequestHandler preFlightRequestHandler() {
return (request, response) -> {
// custom preflight handling (for example, write CORS headers or complete the response)
};
}
@Bean
SecurityFilterChain springSecurity(HttpSecurity http, PreFlightRequestHandler preFlightRequestHandler) {
// tag::preflightRequestHandler[]
http
// ..
.cors((cors) -> cors
.preFlightRequestHandler(preFlightRequestHandler)
);
return http.build();
// end::preflightRequestHandler[]
}
}

View File

@ -0,0 +1,53 @@
/*
* Copyright 2004-present 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
*
* https://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.kt.docs.servlet.integrations.corspreflightrequesthandler
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.security.config.annotation.web.builders.HttpSecurity
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity
import org.springframework.security.config.annotation.web.invoke
import org.springframework.security.web.SecurityFilterChain
import org.springframework.web.cors.PreFlightRequestHandler
@Configuration
@EnableWebSecurity
class CorsPreFlightRequestHandlerExample {
@Bean
fun preFlightRequestHandler(): PreFlightRequestHandler {
return PreFlightRequestHandler { _, _ ->
// custom preflight handling (for example, write CORS headers or complete the response)
}
}
// tag::preflightRequestHandler[]
@Bean
fun springSecurity(http: HttpSecurity, preFlightRequestHandler: PreFlightRequestHandler): SecurityFilterChain {
http {
authorizeHttpRequests {
authorize(anyRequest, authenticated)
}
cors {
this.preFlightRequestHandler = preFlightRequestHandler
}
}
return http.build()
}
// end::preflightRequestHandler[]
}