1
0
mirror of synced 2026-08-06 02:08:01 +00:00

Add Modular Spring Security Configuration

Closes gh-16258
This commit is contained in:
Rob Winch
2025-08-20 09:32:34 -05:00
parent 5c5efc9092
commit a8f045eb50
49 changed files with 3207 additions and 0 deletions
@@ -0,0 +1,99 @@
/*
* 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.reactive.configuration.customizerbeanordering;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
import org.springframework.security.config.web.server.ServerHttpSecurity;
import org.springframework.security.web.server.SecurityWebFilterChain;
import org.springframework.web.reactive.config.EnableWebFlux;
/**
*
*/
@EnableWebFlux
@EnableWebFluxSecurity
@Configuration(proxyBeanMethods = false)
class CustomizerBeanOrderingConfiguration {
// tag::sample[]
@Bean // <4>
SecurityWebFilterChain springSecurity(ServerHttpSecurity http) {
// @formatter:off
http
.authorizeExchange((exchange) -> exchange
.anyExchange().authenticated()
);
return http.build();
// @formatter:on
}
@Bean
@Order(Ordered.LOWEST_PRECEDENCE) // <2>
Customizer<ServerHttpSecurity> userAuthorization() {
// @formatter:off
return (http) -> http
.authorizeExchange((exchange) -> exchange
.pathMatchers("/users/**").hasRole("USER")
);
// @formatter:on
}
@Bean
@Order(Ordered.HIGHEST_PRECEDENCE) // <1>
Customizer<ServerHttpSecurity> adminAuthorization() {
// @formatter:off
return (http) -> http
.authorizeExchange((exchange) -> exchange
.pathMatchers("/admins/**").hasRole("ADMIN")
);
// @formatter:on
}
// <3>
@Bean
Customizer<ServerHttpSecurity.HeaderSpec> contentSecurityPolicy() {
// @formatter:off
return (headers) -> headers
.contentSecurityPolicy((csp) -> csp
.policyDirectives("object-src 'none'")
);
// @formatter:on
}
@Bean
Customizer<ServerHttpSecurity.HeaderSpec> contentTypeOptions() {
// @formatter:off
return (headers) -> headers
.contentTypeOptions(Customizer.withDefaults());
// @formatter:on
}
@Bean
Customizer<ServerHttpSecurity.HttpsRedirectSpec> httpsRedirect() {
// @formatter:off
return Customizer.withDefaults();
// @formatter:on
}
// end::sample[]
}
@@ -0,0 +1,76 @@
/*
* 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.reactive.configuration.customizerbeanordering;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.config.test.SpringTestContext;
import org.springframework.security.config.test.SpringTestContextExtension;
import org.springframework.test.web.reactive.server.WebTestClient;
import org.springframework.test.web.servlet.MockMvc;
import static org.springframework.security.test.web.reactive.server.SecurityMockServerConfigurers.mockUser;
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.user;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
/**
*
*/
@ExtendWith(SpringTestContextExtension.class)
public class CustomizerBeanOrderingTests {
public final SpringTestContext spring = new SpringTestContext(this);
@Autowired
private WebTestClient webTest;
@Test
void authorizationOrdered() throws Exception {
this.spring.register(
CustomizerBeanOrderingConfiguration.class).autowire();
// @formatter:off
this.webTest.mutateWith(mockUser("admin").roles("ADMIN"))
.get()
.uri("https://localhost/admins/1")
.exchange()
.expectStatus().isOk();
this.webTest.mutateWith(mockUser("user").roles("USER"))
.get()
.uri("https://localhost/admins/1")
.exchange()
.expectStatus().isForbidden();
this.webTest.mutateWith(mockUser("user").roles("USER"))
.get()
.uri("https://localhost/users/1")
.exchange()
.expectStatus().isOk();
this.webTest.mutateWith(mockUser("user").roles("OTHER"))
.get()
.uri("https://localhost/users/1")
.exchange()
.expectStatus().isForbidden();
this.webTest.mutateWith(mockUser("authenticated").roles("OTHER"))
.get()
.uri("https://localhost/other")
.exchange()
.expectStatus().isOk();
// @formatter:on
}
}
@@ -0,0 +1,61 @@
/*
* 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.reactive.configuration.serverhttpsecuritycustomizerbean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
import org.springframework.security.config.web.server.ServerHttpSecurity;
import org.springframework.security.web.server.SecurityWebFilterChain;
/**
*
*/
@EnableWebFluxSecurity
@Configuration(proxyBeanMethods = false)
class ServerHttpSecurityCustomizerBeanConfiguration {
@Bean
SecurityWebFilterChain springSecurity(ServerHttpSecurity http) {
// @formatter:off
http
.authorizeExchange((exchange) -> exchange
.anyExchange().authenticated()
);
return http.build();
// @formatter:on
}
// tag::httpSecurityCustomizer[]
@Bean
Customizer<ServerHttpSecurity> httpSecurityCustomizer() {
// @formatter:off
return (http) -> http
.headers((headers) -> headers
.contentSecurityPolicy((csp) -> csp
// <1>
.policyDirectives("object-src 'none'")
)
)
// <2>
.redirectToHttps(Customizer.withDefaults());
// @formatter:on
}
// end::httpSecurityCustomizer[]
}
@@ -0,0 +1,56 @@
/*
* 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.reactive.configuration.serverhttpsecuritycustomizerbean;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.config.test.SpringTestContext;
import org.springframework.security.config.test.SpringTestContextExtension;
import org.springframework.test.web.reactive.server.WebTestClient;
import static org.assertj.core.api.Assertions.assertThat;
/**
*
*/
@ExtendWith(SpringTestContextExtension.class)
public class ServerHttpSecurityCustomizerBeanTests {
public final SpringTestContext spring = new SpringTestContext(this);
@Autowired
private WebTestClient webTest;
@Test
void httpSecurityCustomizer() throws Exception {
this.spring.register(
ServerHttpSecurityCustomizerBeanConfiguration.class).autowire();
// @formatter:off
this.webTest
.get()
.uri("http://localhost/")
.exchange()
.expectHeader().location("https://localhost/")
.expectHeader()
.value("Content-Security-Policy", csp ->
assertThat(csp).isEqualTo("object-src 'none'")
);
// @formatter:on
}
}
@@ -0,0 +1,58 @@
/*
* 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.reactive.configuration.toplevelcustomizerbean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
import org.springframework.security.config.web.server.ServerHttpSecurity;
import org.springframework.security.web.server.SecurityWebFilterChain;
import org.springframework.web.reactive.config.EnableWebFlux;
/**
*
*/
@EnableWebFluxSecurity
@Configuration(proxyBeanMethods = false)
public class TopLevelCustomizerBeanConfiguration {
@Bean
SecurityWebFilterChain springSecurity(ServerHttpSecurity http) {
// @formatter:off
http
.authorizeExchange((exchange) -> exchange
.anyExchange().authenticated()
);
return http.build();
// @formatter:on
}
// tag::headersCustomizer[]
@Bean
Customizer<ServerHttpSecurity.HeaderSpec> headersSecurity() {
// @formatter:off
return (headers) -> headers
.contentSecurityPolicy((csp) -> csp
// <1>
.policyDirectives("object-src 'none'")
);
// @formatter:on
}
// end::headersCustomizer[]
}
@@ -0,0 +1,61 @@
/*
* 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.reactive.configuration.toplevelcustomizerbean;
import org.jetbrains.annotations.NotNull;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.config.test.SpringTestContext;
import org.springframework.security.config.test.SpringTestContextExtension;
import org.springframework.test.web.reactive.server.WebTestClient;
import org.springframework.test.web.servlet.ResultMatcher;
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header;
/**
*
*/
@ExtendWith(SpringTestContextExtension.class)
public class TopLevelCustomizerBeanTests {
public final SpringTestContext spring = new SpringTestContext(this);
@Autowired
private WebTestClient webTest;
@Test
void headersCustomizer() throws Exception {
this.spring.register(TopLevelCustomizerBeanConfiguration.class).autowire();
// @formatter:off
this.webTest
.get()
.uri("http://localhost/")
.exchange()
.expectHeader()
.value("Content-Security-Policy", csp ->
assertThat(csp).isEqualTo("object-src 'none'")
);
// @formatter:on
}
private static @NotNull ResultMatcher cspIsObjectSrcNone() {
return header().string("Content-Security-Policy", "object-src 'none'");
}
}
@@ -0,0 +1,102 @@
/*
* 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.configuration.customizerbeanordering;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.ThrowingCustomizer;
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.configurers.HeadersConfigurer;
import org.springframework.security.config.annotation.web.configurers.HttpsRedirectConfigurer;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
/**
*
*/
@EnableWebMvc
@EnableWebSecurity
@Configuration(proxyBeanMethods = false)
class CustomizerBeanOrderingConfiguration {
// tag::sample[]
@Bean // <4>
SecurityFilterChain springSecurity(HttpSecurity http) throws Exception {
// @formatter:off
http
.authorizeHttpRequests((requests) -> requests
.anyRequest().authenticated()
);
return http.build();
// @formatter:on
}
@Bean
@Order(Ordered.LOWEST_PRECEDENCE) // <2>
ThrowingCustomizer<HttpSecurity> userAuthorization() {
// @formatter:off
return (http) -> http
.authorizeHttpRequests((requests) -> requests
.requestMatchers("/users/**").hasRole("USER")
);
// @formatter:on
}
@Bean
@Order(Ordered.HIGHEST_PRECEDENCE) // <1>
ThrowingCustomizer<HttpSecurity> adminAuthorization() {
// @formatter:off
return (http) -> http
.authorizeHttpRequests((requests) -> requests
.requestMatchers("/admins/**").hasRole("ADMIN")
);
// @formatter:on
}
// <3>
@Bean
Customizer<HeadersConfigurer<HttpSecurity>> contentSecurityPolicy() {
// @formatter:off
return (headers) -> headers
.contentSecurityPolicy((csp) -> csp
.policyDirectives("object-src 'none'")
);
// @formatter:on
}
@Bean
Customizer<HeadersConfigurer<HttpSecurity>> contentTypeOptions() {
// @formatter:off
return (headers) -> headers
.contentTypeOptions(Customizer.withDefaults());
// @formatter:on
}
@Bean
Customizer<HttpsRedirectConfigurer<HttpSecurity>> httpsRedirect() {
// @formatter:off
return Customizer.withDefaults();
// @formatter:on
}
// end::sample[]
}
@@ -0,0 +1,64 @@
/*
* 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.configuration.customizerbeanordering;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.config.test.SpringTestContext;
import org.springframework.security.config.test.SpringTestContextExtension;
import org.springframework.test.web.servlet.MockMvc;
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.user;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
/**
*
*/
@ExtendWith(SpringTestContextExtension.class)
public class CustomizerBeanOrderingTests {
public final SpringTestContext spring = new SpringTestContext(this).mockMvcAfterSpringSecurityOk();
@Autowired
private MockMvc mockMvc;
@Test
void authorizationOrdered() throws Exception {
this.spring.register(
CustomizerBeanOrderingConfiguration.class).autowire();
// @formatter:off
this.mockMvc
.perform(get("https://localhost/admins/1").with(user("admin").roles("ADMIN")))
.andExpect(status().isOk());
this.mockMvc
.perform(get("https://localhost/admins/1").with(user("user").roles("USER")))
.andExpect(status().isForbidden());
this.mockMvc
.perform(get("https://localhost/users/1").with(user("user").roles("USER")))
.andExpect(status().isOk());
this.mockMvc
.perform(get("https://localhost/users/1").with(user("user").roles("OTHER")))
.andExpect(status().isForbidden());
this.mockMvc
.perform(get("https://localhost/other").with(user("authenticated").roles("OTHER")))
.andExpect(status().isOk());
// @formatter:on
}
}
@@ -0,0 +1,77 @@
/*
* 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.configuration.httpsecuritycustomizerbean;
import org.jetbrains.annotations.NotNull;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.ThrowingCustomizer;
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.configurers.HeadersConfigurer;
import org.springframework.security.config.test.SpringTestContext;
import org.springframework.security.config.test.SpringTestContextExtension;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.ResultMatcher;
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header;
/**
*
*/
@EnableWebSecurity
@Configuration(proxyBeanMethods = false)
class HttpSecurityCustomizerBeanConfiguration {
@Bean
SecurityFilterChain springSecurity(HttpSecurity http) throws Exception {
// @formatter:off
http
.authorizeHttpRequests((requests) -> requests
.anyRequest().authenticated()
);
return http.build();
// @formatter:on
}
// tag::httpSecurityCustomizer[]
@Bean
ThrowingCustomizer<HttpSecurity> httpSecurityCustomizer() {
// @formatter:off
return (http) -> http
.headers((headers) -> headers
.contentSecurityPolicy((csp) -> csp
// <1>
.policyDirectives("object-src 'none'")
)
)
// <2>
.redirectToHttps(Customizer.withDefaults());
// @formatter:on
}
// end::httpSecurityCustomizer[]
}
@@ -0,0 +1,74 @@
/*
* 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.configuration.httpsecuritycustomizerbean;
import org.jetbrains.annotations.NotNull;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.ThrowingCustomizer;
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.configurers.HeadersConfigurer;
import org.springframework.security.config.test.SpringTestContext;
import org.springframework.security.config.test.SpringTestContextExtension;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.ResultMatcher;
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header;
/**
*
*/
@ExtendWith(SpringTestContextExtension.class)
public class HttpSecurityCustomizerBeanTests {
public final SpringTestContext spring = new SpringTestContext(this);
@Autowired
private MockMvc mockMvc;
@Test
void httpSecurityCustomizer() throws Exception {
this.spring.register(HttpSecurityCustomizerBeanConfiguration.class).autowire();
// @formatter:off
this.mockMvc
.perform(get("/"))
.andExpect(redirectsToHttps());
// headers are not sent back as a part of the redirect to https, so a separate request is necessary
this.mockMvc.perform(get("https://localhost/"))
.andExpect(cspIsObjectSrcNone());
// @formatter:on
}
private static @NotNull ResultMatcher redirectsToHttps() {
return mvcResult -> assertThat(
mvcResult.getResponse().getRedirectedUrl()).startsWith("https://");
}
private static @NotNull ResultMatcher cspIsObjectSrcNone() {
return header().string("Content-Security-Policy", "object-src 'none'");
}
}
@@ -0,0 +1,71 @@
/*
* 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.configuration.toplevelcustomizerbean;
import org.jetbrains.annotations.NotNull;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.ThrowingCustomizer;
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.configurers.HeadersConfigurer;
import org.springframework.security.config.test.SpringTestContext;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.ResultMatcher;
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header;
/**
*
*/
@EnableWebSecurity
@Configuration(proxyBeanMethods = false)
public class TopLevelCustomizerBeanConfiguration {
@Bean
SecurityFilterChain springSecurity(HttpSecurity http) throws Exception {
// @formatter:off
http
.authorizeHttpRequests((requests) -> requests
.anyRequest().authenticated()
);
return http.build();
// @formatter:on
}
// tag::headersCustomizer[]
@Bean
Customizer<HeadersConfigurer<HttpSecurity>> headersSecurity() {
// @formatter:off
return (headers) -> headers
.contentSecurityPolicy((csp) -> csp
// <1>
.policyDirectives("object-src 'none'")
);
// @formatter:on
}
// end::headersCustomizer[]
}
@@ -0,0 +1,66 @@
/*
* 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.configuration.toplevelcustomizerbean;
import org.jetbrains.annotations.NotNull;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.ThrowingCustomizer;
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.configurers.HeadersConfigurer;
import org.springframework.security.config.test.SpringTestContext;
import org.springframework.security.config.test.SpringTestContextExtension;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.ResultMatcher;
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header;
/**
*
*/
@ExtendWith(SpringTestContextExtension.class)
public class TopLevelCustomizerBeanTests {
public final SpringTestContext spring = new SpringTestContext(this);
@Autowired
private MockMvc mockMvc;
@Test
void headersCustomizer() throws Exception {
this.spring.register(TopLevelCustomizerBeanConfiguration.class).autowire();
// @formatter:off
this.mockMvc
.perform(get("/"))
.andExpect(cspIsObjectSrcNone());
// @formatter:on
}
private static @NotNull ResultMatcher cspIsObjectSrcNone() {
return header().string("Content-Security-Policy", "object-src 'none'");
}
}
@@ -0,0 +1,104 @@
/*
* 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.reactive.configuration.customizerbeanordering
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.core.Ordered
import org.springframework.core.annotation.Order
import org.springframework.security.config.Customizer
import org.springframework.security.config.ThrowingCustomizer
import org.springframework.security.config.annotation.web.builders.HttpSecurity
import org.springframework.security.config.annotation.web.configurers.HeadersConfigurer
import org.springframework.security.config.annotation.web.configurers.HttpsRedirectConfigurer
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity
import org.springframework.security.config.web.server.ServerHttpSecurity
import org.springframework.security.web.server.SecurityWebFilterChain
import org.springframework.security.web.server.util.matcher.ServerWebExchangeMatchers.anyExchange
/**
*
*/
@EnableWebFluxSecurity
@Configuration(proxyBeanMethods = false)
internal class CustomizerBeanOrderingConfiguration {
// tag::sample[]
@Bean // <4>
fun springSecurity(http: ServerHttpSecurity): SecurityWebFilterChain {
// @formatter:off
http
.authorizeExchange({ exchanges -> exchanges
.anyExchange().authenticated()
})
return http.build()
// @formatter:on
}
@Bean
@Order(Ordered.LOWEST_PRECEDENCE) // <2>
fun userAuthorization(): Customizer<ServerHttpSecurity> {
// @formatter:off
return Customizer { http -> http
.authorizeExchange { exchanges -> exchanges
.pathMatchers("/users/**").hasRole("USER")
}
}
// @formatter:on
}
@Bean
@Order(Ordered.HIGHEST_PRECEDENCE) // <1>
fun adminAuthorization(): Customizer<ServerHttpSecurity> {
// @formatter:off
return ThrowingCustomizer { http -> http
.authorizeExchange { exchanges -> exchanges
.pathMatchers("/admins/**").hasRole("ADMIN")
}
}
// @formatter:on
}
// <3>
@Bean
fun contentSecurityPolicy(): Customizer<ServerHttpSecurity.HeaderSpec> {
// @formatter:off
return Customizer { headers -> headers
.contentSecurityPolicy { csp -> csp
.policyDirectives("object-src 'none'")
}
}
// @formatter:on
}
@Bean
fun contentTypeOptions(): Customizer<ServerHttpSecurity.HeaderSpec> {
// @formatter:off
return Customizer { headers -> headers
.contentTypeOptions(Customizer.withDefaults())
}
// @formatter:on
}
@Bean
fun httpsRedirect(): Customizer<ServerHttpSecurity.HttpsRedirectSpec> {
// @formatter:off
return Customizer.withDefaults()
// @formatter:on
}
// end::sample[]
}
@@ -0,0 +1,74 @@
/*
* 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.reactive.configuration.customizerbeanordering
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.ExtendWith
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.security.config.test.SpringTestContext
import org.springframework.security.config.test.SpringTestContextExtension
import org.springframework.security.test.web.reactive.server.SecurityMockServerConfigurers.mockUser
import org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors
import org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.user
import org.springframework.test.web.reactive.server.WebTestClient
import org.springframework.test.web.servlet.MockMvc
import org.springframework.test.web.servlet.get
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders
import org.springframework.test.web.servlet.result.MockMvcResultMatchers
/**
*
*/
@ExtendWith(SpringTestContextExtension::class)
class CustomizerBeanOrderingTests {
@JvmField
val spring = SpringTestContext(this)
@Autowired
lateinit var webTest: WebTestClient
@Test
fun authorizationOrdered() {
this.spring.register(CustomizerBeanOrderingConfiguration::class.java).autowire()
// @formatter:off
this.webTest.mutateWith(mockUser("admin").roles("ADMIN"))
.get()
.uri("https://localhost/admins/1")
.exchange()
.expectStatus().isOk
this.webTest.mutateWith(mockUser("user").roles("USER"))
.get()
.uri("https://localhost/admins/1")
.exchange()
.expectStatus().isForbidden
this.webTest.mutateWith(mockUser("user").roles("USER"))
.get()
.uri("https://localhost/users/1")
.exchange()
.expectStatus().isOk
this.webTest.mutateWith(mockUser("user").roles("OTHER"))
.get()
.uri("https://localhost/users/1")
.exchange()
.expectStatus().isForbidden
this.webTest.mutateWith(mockUser("other").roles("OTHER"))
.get()
.uri("https://localhost/other")
.exchange()
.expectStatus().isOk
// @formatter:on
}
}
@@ -0,0 +1,99 @@
/*
* 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.reactive.configuration.dslbeanordering
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.core.Ordered
import org.springframework.core.annotation.Order
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity
import org.springframework.security.config.web.server.*
import org.springframework.security.web.server.SecurityWebFilterChain
/**
*
*/
@EnableWebFluxSecurity
@Configuration(proxyBeanMethods = false)
internal class DslBeanOrderingConfiguration {
// tag::sample[]
// All of the Java Modular Configuration is applied first <1>
@Bean // <5>
fun springSecurity(http: ServerHttpSecurity): SecurityWebFilterChain {
// @formatter:off
return http {
authorizeExchange {
authorize(anyExchange, authenticated)
}
}
// @formatter:on
}
@Bean
@Order(Ordered.LOWEST_PRECEDENCE) // <3>
fun userAuthorization(): ServerHttpSecurityDsl.() -> Unit {
// @formatter:off
return {
authorizeExchange {
authorize("/users/**", hasRole("USER"))
}
}
// @formatter:on
}
@Bean
@Order(Ordered.HIGHEST_PRECEDENCE) // <2>
fun adminAuthorization(): ServerHttpSecurityDsl.() -> Unit {
// @formatter:off
return {
authorizeExchange {
authorize("/admins/**", hasRole("ADMIN"))
}
}
// @formatter:on
}
// <4>
@Bean
fun contentSecurityPolicy(): ServerHeadersDsl.() -> Unit {
// @formatter:off
return {
contentSecurityPolicy {
policyDirectives = "object-src 'none'"
}
}
// @formatter:on
}
@Bean
fun contentTypeOptions(): ServerHeadersDsl.() -> Unit {
// @formatter:off
return {
contentTypeOptions { }
}
// @formatter:on
}
@Bean
fun httpsRedirect(): ServerHttpsRedirectDsl.() -> Unit {
// @formatter:off
return { }
// @formatter:on
}
// end::sample[]
}
@@ -0,0 +1,72 @@
/*
* 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.reactive.configuration.dslbeanordering
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.ExtendWith
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.security.config.test.SpringTestContext
import org.springframework.security.config.test.SpringTestContextExtension
import org.springframework.security.kt.docs.servlet.configuration.customizerbeanordering.CustomizerBeanOrderingConfiguration
import org.springframework.security.test.web.reactive.server.SecurityMockServerConfigurers.mockUser
import org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.user
import org.springframework.test.web.reactive.server.WebTestClient
import org.springframework.test.web.servlet.MockMvc
import org.springframework.test.web.servlet.get
/**
*
*/
@ExtendWith(SpringTestContextExtension::class)
class DslBeanOrderingTests {
@JvmField
val spring = SpringTestContext(this).mockMvcAfterSpringSecurityOk()
@Autowired
lateinit var webTest: WebTestClient
@Test
fun dslOrdered() {
this.spring.register(org.springframework.security.kt.docs.reactive.configuration.customizerbeanordering.CustomizerBeanOrderingConfiguration::class.java).autowire()
// @formatter:off
this.webTest.mutateWith(mockUser("admin").roles("ADMIN"))
.get()
.uri("https://localhost/admins/1")
.exchange()
.expectStatus().isOk
this.webTest.mutateWith(mockUser("user").roles("USER"))
.get()
.uri("https://localhost/admins/1")
.exchange()
.expectStatus().isForbidden
this.webTest.mutateWith(mockUser("user").roles("USER"))
.get()
.uri("https://localhost/users/1")
.exchange()
.expectStatus().isOk
this.webTest.mutateWith(mockUser("user").roles("OTHER"))
.get()
.uri("https://localhost/users/1")
.exchange()
.expectStatus().isForbidden
this.webTest.mutateWith(mockUser("other").roles("OTHER"))
.get()
.uri("https://localhost/other")
.exchange()
.expectStatus().isOk
// @formatter:on
}
}
@@ -0,0 +1,44 @@
package org.springframework.security.kt.docs.reactive.configuration.serverhttpsecuritycustomizerbean
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.security.config.Customizer
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity
import org.springframework.security.config.web.server.ServerHttpSecurity
import org.springframework.security.web.server.SecurityWebFilterChain
@EnableWebFluxSecurity
@Configuration(proxyBeanMethods = false)
class ServerHttpSecurityCustomizerBeanConfiguration {
@Bean
fun springSecurity(http: ServerHttpSecurity): SecurityWebFilterChain {
// @formatter:off
http
.authorizeExchange({ exchanges -> exchanges
.anyExchange().authenticated()
})
return http.build()
// @formatter:on
}
// tag::httpSecurityCustomizer[]
@Bean
fun httpSecurityCustomizer(): Customizer<ServerHttpSecurity> {
// @formatter:off
return Customizer { http -> http
.headers { headers -> headers
.contentSecurityPolicy { csp -> csp
// <1>
.policyDirectives("object-src 'none'")
}
}
// <2>
.redirectToHttps(Customizer.withDefaults())
}
// @formatter:on
}
// end::httpSecurityCustomizer[]
}
@@ -0,0 +1,37 @@
package org.springframework.security.kt.docs.reactive.configuration.serverhttpsecuritycustomizerbean
import org.assertj.core.api.Assertions
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.ExtendWith
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.security.config.test.SpringTestContext
import org.springframework.security.config.test.SpringTestContextExtension
import org.springframework.test.web.reactive.server.WebTestClient
import java.util.function.Consumer
@ExtendWith(SpringTestContextExtension::class)
class ServerHttpSecurityCustomizerBeanTests {
@JvmField
val spring = SpringTestContext(this)
@Autowired
lateinit var webTest: WebTestClient
@Test
fun `serverhttpsecurity customizer config`() {
this.spring.register(ServerHttpSecurityCustomizerBeanConfiguration::class.java).autowire()
// @formatter:off
this.webTest
.get()
.uri("http://localhost/")
.exchange()
.expectHeader().location("https://localhost/")
.expectHeader()
.value("Content-Security-Policy", Consumer { csp ->
assertThat(csp).isEqualTo("object-src 'none'")
})
// @formatter:on
}
}
@@ -0,0 +1,42 @@
package org.springframework.security.kt.docs.reactive.configuration.serverhttpsecuritydslbean
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity
import org.springframework.security.config.web.server.ServerHttpSecurity
import org.springframework.security.config.web.server.ServerHttpSecurityDsl
import org.springframework.security.config.web.server.invoke
import org.springframework.security.web.server.SecurityWebFilterChain
import org.springframework.web.servlet.config.annotation.EnableWebMvc
@EnableWebFluxSecurity
@Configuration(proxyBeanMethods = false)
class ServerHttpSecurityDslBeanConfiguration {
@Bean
fun springSecurity(http: ServerHttpSecurity): SecurityWebFilterChain {
return http {
authorizeExchange {
authorize(anyExchange, authenticated)
}
}
}
// tag::httpSecurityDslBean[]
@Bean
fun httpSecurityDslBean(): ServerHttpSecurityDsl.() -> Unit {
return {
headers {
contentSecurityPolicy {
// <1>
policyDirectives = "object-src 'none'"
}
}
// <2>
redirectToHttps { }
}
}
// end::httpSecurityDslBean[]
}
@@ -0,0 +1,38 @@
package org.springframework.security.kt.docs.reactive.configuration.serverhttpsecuritydslbean
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.ExtendWith
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.security.config.test.SpringTestContext
import org.springframework.security.config.test.SpringTestContextExtension
import org.springframework.test.web.reactive.server.WebTestClient
import org.springframework.test.web.servlet.MockMvc
import org.springframework.test.web.servlet.get
import java.util.function.Consumer
@ExtendWith(SpringTestContextExtension::class)
class ServerHttpSecurityDslBeanTests {
@JvmField
val spring = SpringTestContext(this)
@Autowired
lateinit var webTest: WebTestClient
@Test
fun `ServerHttpSecurityDslBean`() {
this.spring.register(ServerHttpSecurityDslBeanConfiguration::class.java).autowire()
// @formatter:off
this.webTest
.get()
.uri("http://localhost/")
.exchange()
.expectHeader().location("https://localhost/")
.expectHeader().value("Content-Security-Policy", Consumer { csp ->
assertThat(csp).isEqualTo("object-src 'none'")
})
// @formatter:on
}
}
@@ -0,0 +1,43 @@
package org.springframework.security.kt.docs.reactive.configuration.toplevelcustomizerbean
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.security.config.Customizer
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.configurers.HeadersConfigurer
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity
import org.springframework.security.config.web.server.ServerHttpSecurity
import org.springframework.security.web.SecurityFilterChain
import org.springframework.security.web.server.SecurityWebFilterChain
@EnableWebFluxSecurity
@Configuration(proxyBeanMethods = false)
class TopLevelCustomizerBeanConfiguration {
@Bean
fun springSecurity(http: ServerHttpSecurity): SecurityWebFilterChain {
// @formatter:off
http
.authorizeExchange({ exchanges -> exchanges
.anyExchange().authenticated()
})
return http.build()
// @formatter:on
}
// tag::headersCustomizer[]
@Bean
fun headersSecurity(): Customizer<ServerHttpSecurity.HeaderSpec> {
// @formatter:off
return Customizer { headers -> headers
.contentSecurityPolicy { csp -> csp
// <1>
.policyDirectives("object-src 'none'")
}
}
// @formatter:on
}
// end::headersCustomizer[]
}
@@ -0,0 +1,35 @@
package org.springframework.security.kt.docs.reactive.configuration.toplevelcustomizerbean
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.ExtendWith
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.security.config.test.SpringTestContext
import org.springframework.security.config.test.SpringTestContextExtension
import org.springframework.test.web.reactive.server.WebTestClient
import java.util.function.Consumer
@ExtendWith(SpringTestContextExtension::class)
class TopLevelCustomizerBeanTests {
@JvmField
val spring = SpringTestContext(this)
@Autowired
lateinit var webTest: WebTestClient
@Test
fun `top level dsl bean`() {
this.spring.register(TopLevelCustomizerBeanConfiguration::class.java).autowire()
// @formatter:off
this.webTest
.get()
.uri("http://localhost/")
.exchange()
.expectHeader().value("Content-Security-Policy", Consumer { csp ->
assertThat(csp).isEqualTo("object-src 'none'")
})
// @formatter:on
}
}
@@ -0,0 +1,36 @@
package org.springframework.security.kt.docs.reactive.configuration.topleveldslbean
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity
import org.springframework.security.config.web.server.ServerHeadersDsl
import org.springframework.security.config.web.server.ServerHttpSecurity
import org.springframework.security.config.web.server.invoke
import org.springframework.security.web.server.SecurityWebFilterChain
@EnableWebFluxSecurity
@Configuration(proxyBeanMethods = false)
class TopLevelDslBeanConfiguration {
@Bean
fun springSecurity(http: ServerHttpSecurity): SecurityWebFilterChain {
return http {
authorizeExchange {
authorize(anyExchange, authenticated)
}
}
}
// tag::headersSecurity[]
@Bean
fun headersSecurity(): ServerHeadersDsl.() -> Unit {
return {
contentSecurityPolicy {
// <1>
policyDirectives = "object-src 'none'"
}
}
}
// end::headersSecurity[]
}
@@ -0,0 +1,37 @@
package org.springframework.security.kt.docs.reactive.configuration.topleveldslbean
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.ExtendWith
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.security.config.test.SpringTestContext
import org.springframework.security.config.test.SpringTestContextExtension
import org.springframework.test.web.reactive.server.WebTestClient
import org.springframework.test.web.servlet.MockMvc
import org.springframework.test.web.servlet.get
import java.util.function.Consumer
@ExtendWith(SpringTestContextExtension::class)
class TopLevelDslBeanTests {
@JvmField
val spring = SpringTestContext(this)
@Autowired
lateinit var webTest: WebTestClient
@Test
fun `HttpSecurityDslBean`() {
this.spring.register(TopLevelDslBeanConfiguration::class.java).autowire()
// @formatter:off
this.webTest
.get()
.uri("http://localhost/")
.exchange()
.expectHeader().value("Content-Security-Policy", Consumer { csp ->
assertThat(csp).isEqualTo("object-src 'none'")
})
// @formatter:on
}
}
@@ -0,0 +1,104 @@
/*
* 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.configuration.customizerbeanordering
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.core.Ordered
import org.springframework.core.annotation.Order
import org.springframework.security.config.Customizer
import org.springframework.security.config.ThrowingCustomizer
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.configurers.AuthorizeHttpRequestsConfigurer.AuthorizationManagerRequestMatcherRegistry
import org.springframework.security.config.annotation.web.configurers.HeadersConfigurer
import org.springframework.security.config.annotation.web.configurers.HttpsRedirectConfigurer
import org.springframework.security.web.SecurityFilterChain
import org.springframework.web.servlet.config.annotation.EnableWebMvc
/**
*
*/
@EnableWebMvc
@EnableWebSecurity
@Configuration(proxyBeanMethods = false)
internal class CustomizerBeanOrderingConfiguration {
// tag::sample[]
@Bean // <4>
fun springSecurity(http: HttpSecurity): SecurityFilterChain {
// @formatter:off
http
.authorizeHttpRequests({ requests -> requests
.anyRequest().authenticated()
})
return http.build()
// @formatter:on
}
@Bean
@Order(Ordered.LOWEST_PRECEDENCE) // <2>
fun userAuthorization(): ThrowingCustomizer<HttpSecurity> {
// @formatter:off
return ThrowingCustomizer { http -> http
.authorizeHttpRequests { requests -> requests
.requestMatchers("/users/**").hasRole("USER")
}
}
// @formatter:on
}
@Bean
@Order(Ordered.HIGHEST_PRECEDENCE) // <1>
fun adminAuthorization(): ThrowingCustomizer<HttpSecurity> {
// @formatter:off
return ThrowingCustomizer { http -> http
.authorizeHttpRequests { requests -> requests
.requestMatchers("/admins/**").hasRole("ADMIN")
}
}
// @formatter:on
}
// <3>
@Bean
fun contentSecurityPolicy(): Customizer<HeadersConfigurer<HttpSecurity>> {
// @formatter:off
return Customizer { headers -> headers
.contentSecurityPolicy { csp -> csp
.policyDirectives("object-src 'none'")
}
}
// @formatter:on
}
@Bean
fun contentTypeOptions(): Customizer<HeadersConfigurer<HttpSecurity>> {
// @formatter:off
return Customizer { headers -> headers
.contentTypeOptions(Customizer.withDefaults())
}
// @formatter:on
}
@Bean
fun httpsRedirect(): Customizer<HttpsRedirectConfigurer<HttpSecurity>> {
// @formatter:off
return Customizer.withDefaults<HttpsRedirectConfigurer<HttpSecurity>>()
// @formatter:on
}
// end::sample[]
}
@@ -0,0 +1,72 @@
/*
* 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.configuration.customizerbeanordering
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.ExtendWith
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.security.config.test.SpringTestContext
import org.springframework.security.config.test.SpringTestContextExtension
import org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors
import org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.user
import org.springframework.test.web.servlet.MockMvc
import org.springframework.test.web.servlet.get
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders
import org.springframework.test.web.servlet.result.MockMvcResultMatchers
/**
*
*/
@ExtendWith(SpringTestContextExtension::class)
class CustomizerBeanOrderingTests {
@JvmField
val spring = SpringTestContext(this).mockMvcAfterSpringSecurityOk()
@Autowired
lateinit var mockMvc: MockMvc
@Test
fun authorizationOrdered() {
this.spring.register(CustomizerBeanOrderingConfiguration::class.java).autowire()
// @formatter:off
this.mockMvc.get("https://localhost/admins/1") {
with(user("admin").roles("ADMIN"))
}.andExpect {
status { isOk() }
}
this.mockMvc.get("https://localhost/admins/1") {
with(user("user").roles("USER"))
}.andExpect {
status { isForbidden() }
}
this.mockMvc.get("https://localhost/users/1") {
with(user("user").roles("USER"))
}.andExpect {
status { isOk() }
}
this.mockMvc.get("https://localhost/users/1") {
with(user("noUserRole").roles("OTHER"))
}.andExpect {
status { isForbidden() }
}
this.mockMvc.get("https://localhost/other") {
with(user("authenticated").roles("OTHER"))
}.andExpect {
status { isOk() }
}
// @formatter:on
}
}
@@ -0,0 +1,110 @@
/*
* 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.configuration.dslbeanordering
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.core.Ordered
import org.springframework.core.annotation.Order
import org.springframework.security.config.Customizer
import org.springframework.security.config.ThrowingCustomizer
import org.springframework.security.config.annotation.web.HeadersDsl
import org.springframework.security.config.annotation.web.HttpSecurityDsl
import org.springframework.security.config.annotation.web.HttpsRedirectDsl
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.configurers.HeadersConfigurer
import org.springframework.security.config.annotation.web.configurers.HttpsRedirectConfigurer
import org.springframework.security.config.annotation.web.invoke
import org.springframework.security.web.SecurityFilterChain
import org.springframework.web.servlet.config.annotation.EnableWebMvc
/**
*
*/
@EnableWebMvc
@EnableWebSecurity
@Configuration(proxyBeanMethods = false)
internal class DslBeanOrderingConfiguration {
// tag::sample[]
// All of the Java Modular Configuration is applied first <1>
@Bean // <5>
fun springSecurity(http: HttpSecurity): SecurityFilterChain {
// @formatter:off
http {
authorizeHttpRequests {
authorize(anyRequest, authenticated)
}
}
return http.build()
// @formatter:on
}
@Bean
@Order(Ordered.LOWEST_PRECEDENCE) // <3>
fun userAuthorization(): HttpSecurityDsl.() -> Unit {
// @formatter:off
return {
authorizeHttpRequests {
authorize("/users/**", hasRole("USER"))
}
}
// @formatter:on
}
@Bean
@Order(Ordered.HIGHEST_PRECEDENCE) // <2>
fun adminAuthorization(): HttpSecurityDsl.() -> Unit {
// @formatter:off
return {
authorizeHttpRequests {
authorize("/admins/**", hasRole("ADMIN"))
}
}
// @formatter:on
}
// <4>
@Bean
fun contentSecurityPolicy(): HeadersDsl.() -> Unit {
// @formatter:off
return {
contentSecurityPolicy {
policyDirectives = "object-src 'none'"
}
}
// @formatter:on
}
@Bean
fun contentTypeOptions(): HeadersDsl.() -> Unit {
// @formatter:off
return {
contentTypeOptions { }
}
// @formatter:on
}
@Bean
fun httpsRedirect(): HttpsRedirectDsl.() -> Unit {
// @formatter:off
return { }
// @formatter:on
}
// end::sample[]
}
@@ -0,0 +1,70 @@
/*
* 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.configuration.dslbeanordering
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.ExtendWith
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.security.config.test.SpringTestContext
import org.springframework.security.config.test.SpringTestContextExtension
import org.springframework.security.kt.docs.servlet.configuration.customizerbeanordering.CustomizerBeanOrderingConfiguration
import org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.user
import org.springframework.test.web.servlet.MockMvc
import org.springframework.test.web.servlet.get
/**
*
*/
@ExtendWith(SpringTestContextExtension::class)
class DslBeanOrderingTests {
@JvmField
val spring = SpringTestContext(this).mockMvcAfterSpringSecurityOk()
@Autowired
lateinit var mockMvc: MockMvc
@Test
fun dslOrdered() {
this.spring.register(DslBeanOrderingConfiguration::class.java).autowire()
// @formatter:off
this.mockMvc.get("https://localhost/admins/1") {
with(user("admin").roles("ADMIN"))
}.andExpect {
status { isOk() }
}
this.mockMvc.get("https://localhost/admins/1") {
with(user("user").roles("USER"))
}.andExpect {
status { isForbidden() }
}
this.mockMvc.get("https://localhost/users/1") {
with(user("user").roles("USER"))
}.andExpect {
status { isOk() }
}
this.mockMvc.get("https://localhost/users/1") {
with(user("noUserRole").roles("OTHER"))
}.andExpect {
status { isForbidden() }
}
this.mockMvc.get("https://localhost/other") {
with(user("authenticated").roles("OTHER"))
}.andExpect {
status { isOk() }
}
// @formatter:on
}
}
@@ -0,0 +1,46 @@
package org.springframework.security.kt.docs.servlet.configuration.httpsecuritycustomizerbean
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.security.config.Customizer
import org.springframework.security.config.ThrowingCustomizer
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.configurers.HeadersConfigurer
import org.springframework.security.config.annotation.web.invoke
import org.springframework.security.web.SecurityFilterChain
@EnableWebSecurity
@Configuration(proxyBeanMethods = false)
class HttpSecurityCustomizerBeanConfiguration {
@Bean
fun springSecurity(http: HttpSecurity): SecurityFilterChain {
http {
authorizeHttpRequests {
authorize(anyRequest, authenticated)
}
}
return http.build()
}
// tag::httpSecurityCustomizer[]
@Bean
fun httpSecurityCustomizer(): ThrowingCustomizer<HttpSecurity> {
// @formatter:off
return ThrowingCustomizer { http -> http
.headers { headers -> headers
.contentSecurityPolicy { csp -> csp
// <1>
.policyDirectives("object-src 'none'")
}
}
// <2>
.redirectToHttps(Customizer.withDefaults())
}
// @formatter:on
}
// end::httpSecurityCustomizer[]
}
@@ -0,0 +1,35 @@
package org.springframework.security.kt.docs.servlet.configuration.httpsecuritycustomizerbean
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.ExtendWith
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.security.config.test.SpringTestContext
import org.springframework.security.config.test.SpringTestContextExtension
import org.springframework.test.web.servlet.MockMvc
import org.springframework.test.web.servlet.get
@ExtendWith(SpringTestContextExtension::class)
class HttpSecurityCustomizerBeanTests {
@JvmField
val spring = SpringTestContext(this)
@Autowired
lateinit var mockMvc: MockMvc
@Test
fun `httpsecurity customizer config`() {
this.spring.register(HttpSecurityCustomizerBeanConfiguration::class.java).autowire()
this.mockMvc.get("/")
.andExpect {
redirectedUrl("https://localhost/")
}
this.mockMvc.get("https://localhost/")
.andExpect {
header {
string("Content-Security-Policy", "object-src 'none'")
}
}
}
}
@@ -0,0 +1,52 @@
package org.springframework.security.kt.docs.servlet.configuration.httpsecuritydslbean
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.ExtendWith
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.security.config.annotation.web.HeadersDsl
import org.springframework.security.config.annotation.web.HttpSecurityDsl
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.config.test.SpringTestContext
import org.springframework.security.config.test.SpringTestContextExtension
import org.springframework.security.web.SecurityFilterChain
import org.springframework.test.web.servlet.MockMvc
import org.springframework.test.web.servlet.get
import org.springframework.web.servlet.config.annotation.EnableWebMvc
@EnableWebMvc
@EnableWebSecurity
@Configuration(proxyBeanMethods = false)
class HttpSecurityDslBeanConfiguration {
@Bean
fun springSecurity(http: HttpSecurity): SecurityFilterChain {
http {
authorizeHttpRequests {
authorize(anyRequest, authenticated)
}
}
return http.build()
}
// tag::httpSecurityDslBean[]
@Bean
fun httpSecurityDslBean(): HttpSecurityDsl.() -> Unit {
return {
headers {
contentSecurityPolicy {
// <1>
policyDirectives = "object-src 'none'"
}
}
// <2>
redirectToHttps { }
}
}
// end::httpSecurityDslBean[]
}
@@ -0,0 +1,36 @@
package org.springframework.security.kt.docs.servlet.configuration.httpsecuritydslbean
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.ExtendWith
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.security.config.test.SpringTestContext
import org.springframework.security.config.test.SpringTestContextExtension
import org.springframework.test.web.servlet.MockMvc
import org.springframework.test.web.servlet.get
@ExtendWith(SpringTestContextExtension::class)
class HttpSecurityDslBeanTests {
@JvmField
val spring = SpringTestContext(this)
@Autowired
lateinit var mockMvc: MockMvc
@Test
fun `HttpSecurityDslBean`() {
this.spring.register(HttpSecurityDslBeanConfiguration::class.java).autowire()
this.mockMvc.get("/")
.andExpect {
redirectedUrl("https://localhost/")
}
this.mockMvc.get("https://localhost/")
.andExpect {
header {
string("Content-Security-Policy", "object-src 'none'")
}
}
}
}
@@ -0,0 +1,42 @@
package org.springframework.security.kt.docs.servlet.configuration.toplevelcustomizerbean
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.security.config.Customizer
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.configurers.HeadersConfigurer
import org.springframework.security.config.annotation.web.invoke
import org.springframework.security.web.SecurityFilterChain
@EnableWebSecurity
@Configuration(proxyBeanMethods = false)
class TopLevelCustomizerBeanConfiguration {
@Bean
fun springSecurity(http: HttpSecurity): SecurityFilterChain {
// @formatter:off
http {
authorizeHttpRequests {
authorize(anyRequest, authenticated)
}
}
return http.build()
// @formatter:on
}
// tag::headersCustomizer[]
@Bean
fun headersSecurity(): Customizer<HeadersConfigurer<HttpSecurity>> {
// @formatter:off
return Customizer { headers -> headers
.contentSecurityPolicy { csp -> csp
// <1>
.policyDirectives("object-src 'none'")
}
}
// @formatter:on
}
// end::headersCustomizer[]
}
@@ -0,0 +1,31 @@
package org.springframework.security.kt.docs.servlet.configuration.toplevelcustomizerbean
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.ExtendWith
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.security.config.test.SpringTestContext
import org.springframework.security.config.test.SpringTestContextExtension
import org.springframework.test.web.servlet.MockMvc
import org.springframework.test.web.servlet.get
@ExtendWith(SpringTestContextExtension::class)
class TopLevelCustomizerBeanTests {
@JvmField
val spring = SpringTestContext(this)
@Autowired
lateinit var mockMvc: MockMvc
@Test
fun `top level dsl bean`() {
this.spring.register(TopLevelCustomizerBeanConfiguration::class.java).autowire()
this.mockMvc.get("/")
.andExpect {
header {
string("Content-Security-Policy", "object-src 'none'")
}
}
}
}
@@ -0,0 +1,40 @@
package org.springframework.security.kt.docs.servlet.configuration.topleveldslbean
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.security.config.annotation.web.HeadersDsl
import org.springframework.security.config.annotation.web.HttpSecurityDsl
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.servlet.config.annotation.EnableWebMvc
@EnableWebMvc
@EnableWebSecurity
@Configuration(proxyBeanMethods = false)
class TopLevelDslBeanConfiguration {
@Bean
fun springSecurity(http: HttpSecurity): SecurityFilterChain {
http {
authorizeHttpRequests {
authorize(anyRequest, authenticated)
}
}
return http.build()
}
// tag::headersSecurity[]
@Bean
fun headersSecurity(): HeadersDsl.() -> Unit {
return {
contentSecurityPolicy {
// <1>
policyDirectives = "object-src 'none'"
}
}
}
// end::headersSecurity[]
}
@@ -0,0 +1,31 @@
package org.springframework.security.kt.docs.servlet.configuration.topleveldslbean
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.ExtendWith
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.security.config.test.SpringTestContext
import org.springframework.security.config.test.SpringTestContextExtension
import org.springframework.test.web.servlet.MockMvc
import org.springframework.test.web.servlet.get
@ExtendWith(SpringTestContextExtension::class)
class TopLevelDslBeanTests {
@JvmField
val spring = SpringTestContext(this)
@Autowired
lateinit var mockMvc: MockMvc
@Test
fun `HttpSecurityDslBean`() {
this.spring.register(TopLevelDslBeanConfiguration::class.java).autowire()
this.mockMvc.get("/")
.andExpect {
header {
string("Content-Security-Policy", "object-src 'none'")
}
}
}
}