diff --git a/config/src/main/java/org/springframework/security/config/annotation/web/configuration/HttpSecurityConfiguration.java b/config/src/main/java/org/springframework/security/config/annotation/web/configuration/HttpSecurityConfiguration.java index 963a218123..831375612c 100644 --- a/config/src/main/java/org/springframework/security/config/annotation/web/configuration/HttpSecurityConfiguration.java +++ b/config/src/main/java/org/springframework/security/config/annotation/web/configuration/HttpSecurityConfiguration.java @@ -38,6 +38,8 @@ import org.springframework.security.config.annotation.web.configurers.DefaultLog import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.context.SecurityContextHolderStrategy; import org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter; +import org.springframework.web.accept.ContentNegotiationStrategy; +import org.springframework.web.accept.HeaderContentNegotiationStrategy; import static org.springframework.security.config.Customizer.withDefaults; @@ -63,6 +65,8 @@ class HttpSecurityConfiguration { private SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder .getContextHolderStrategy(); + private ContentNegotiationStrategy contentNegotiationStrategy = new HeaderContentNegotiationStrategy(); + @Autowired void setObjectPostProcessor(ObjectPostProcessor objectPostProcessor) { this.objectPostProcessor = objectPostProcessor; @@ -83,6 +87,11 @@ class HttpSecurityConfiguration { this.securityContextHolderStrategy = securityContextHolderStrategy; } + @Autowired(required = false) + void setContentNegotiationStrategy(ContentNegotiationStrategy contentNegotiationStrategy) { + this.contentNegotiationStrategy = contentNegotiationStrategy; + } + @Bean(HTTPSECURITY_BEAN_NAME) @Scope("prototype") HttpSecurity httpSecurity() throws Exception { @@ -136,6 +145,7 @@ class HttpSecurityConfiguration { private Map, Object> createSharedObjects() { Map, Object> sharedObjects = new HashMap<>(); sharedObjects.put(ApplicationContext.class, this.context); + sharedObjects.put(ContentNegotiationStrategy.class, this.contentNegotiationStrategy); return sharedObjects; } diff --git a/config/src/test/java/org/springframework/security/config/annotation/web/configuration/HttpSecurityConfigurationTests.java b/config/src/test/java/org/springframework/security/config/annotation/web/configuration/HttpSecurityConfigurationTests.java index 9f8afc2a9d..e808ac1aa6 100644 --- a/config/src/test/java/org/springframework/security/config/annotation/web/configuration/HttpSecurityConfigurationTests.java +++ b/config/src/test/java/org/springframework/security/config/annotation/web/configuration/HttpSecurityConfigurationTests.java @@ -61,12 +61,16 @@ import org.springframework.security.web.header.writers.frameoptions.XFrameOption import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.MvcResult; import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder; +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 static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.atLeastOnce; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.springframework.security.config.Customizer.withDefaults; import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders.formLogin; @@ -313,6 +317,14 @@ public class HttpSecurityConfigurationTests { assertThat(configurer.configure).isTrue(); } + @Test + public void getWhenCustomContentNegotiationStrategyThenUses() throws Exception { + this.spring.register(CustomContentNegotiationStrategyConfig.class).autowire(); + this.mockMvc.perform(get("/")); + verify(CustomContentNegotiationStrategyConfig.CNS, atLeastOnce()) + .resolveMediaTypes(any(NativeWebRequest.class)); + } + @RestController static class NameController { @@ -496,6 +508,30 @@ public class HttpSecurityConfigurationTests { } + @Configuration + @EnableWebSecurity + static class CustomContentNegotiationStrategyConfig { + + @Bean + SecurityFilterChain filterChain(HttpSecurity http) throws Exception { + // @formatter:off + http + .authorizeHttpRequests((requests) -> requests + .anyRequest().authenticated() + ); + // @formatter:on + return http.build(); + } + + static ContentNegotiationStrategy CNS = mock(ContentNegotiationStrategy.class); + + @Bean + static ContentNegotiationStrategy cns() { + return CNS; + } + + } + static class DefaultConfigurer extends AbstractHttpConfigurer { boolean init;