Support nested builder in DSL for reactive apps
Fixes: gh-7107
This commit is contained in:
committed by
Rob Winch
parent
ab6440db10
commit
a288ce4b00
@@ -28,10 +28,10 @@ The following will disable the CORS integration within Spring Security:
|
||||
[source,java]
|
||||
----
|
||||
@Bean
|
||||
SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
|
||||
SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) throws Exception {
|
||||
http
|
||||
// ...
|
||||
.cors().disable();
|
||||
.cors(cors -> cors.disable());
|
||||
return http.build();
|
||||
}
|
||||
----
|
||||
|
||||
@@ -53,12 +53,20 @@ You can easily do this with the following Java Configuration:
|
||||
[source,java]
|
||||
----
|
||||
@Bean
|
||||
SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
|
||||
SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) throws Exception {
|
||||
http
|
||||
// ...
|
||||
.headers()
|
||||
.hsts().disable()
|
||||
.frameOptions().mode(Mode.SAMEORIGIN);
|
||||
.headers(headers ->
|
||||
headers
|
||||
.hsts(hsts ->
|
||||
hsts
|
||||
.disable()
|
||||
)
|
||||
.frameOptions(frameOptions ->
|
||||
frameOptions
|
||||
.mode(Mode.SAMEORIGIN)
|
||||
)
|
||||
);
|
||||
return http.build();
|
||||
}
|
||||
----
|
||||
@@ -72,11 +80,13 @@ If necessary, you can disable all of the HTTP Security response headers with the
|
||||
[source,java]
|
||||
----
|
||||
@Bean
|
||||
SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
|
||||
SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) throws Exception {
|
||||
http
|
||||
// ...
|
||||
.headers()
|
||||
.disable();
|
||||
.headers(headers ->
|
||||
headers
|
||||
.disable()
|
||||
);
|
||||
return http.build();
|
||||
}
|
||||
----
|
||||
@@ -104,11 +114,13 @@ You can also disable cache control using the following Java Configuration:
|
||||
[source,java]
|
||||
----
|
||||
@Bean
|
||||
SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
|
||||
SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) throws Exception {
|
||||
http
|
||||
// ...
|
||||
.headers()
|
||||
.cache().disable();
|
||||
.headers(headers ->
|
||||
headers
|
||||
.cache(cache -> cache.disable())
|
||||
);
|
||||
return http.build();
|
||||
}
|
||||
----
|
||||
@@ -143,11 +155,13 @@ However, if need to disable the header, the following may be used:
|
||||
[source,java]
|
||||
----
|
||||
@Bean
|
||||
SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
|
||||
SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) throws Exception {
|
||||
http
|
||||
// ...
|
||||
.headers()
|
||||
.contentTypeOptions().disable();
|
||||
.headers(headers ->
|
||||
headers
|
||||
.contentTypeOptions(contentTypeOptions -> contentTypeOptions.disable())
|
||||
);
|
||||
return http.build();
|
||||
}
|
||||
----
|
||||
@@ -188,14 +202,18 @@ You can customize HSTS headers with Java Configuration:
|
||||
[source,java]
|
||||
----
|
||||
@Bean
|
||||
SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
|
||||
SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) throws Exception {
|
||||
http
|
||||
// ...
|
||||
.headers()
|
||||
.hsts()
|
||||
.includeSubdomains(true)
|
||||
.preload(true)
|
||||
.maxAge(Duration.ofDays(365));
|
||||
.headers(headers ->
|
||||
headers
|
||||
.hsts(hsts ->
|
||||
hsts
|
||||
.includeSubdomains(true)
|
||||
.preload(true)
|
||||
.maxAge(Duration.ofDays(365))
|
||||
)
|
||||
);
|
||||
return http.build();
|
||||
}
|
||||
----
|
||||
@@ -232,12 +250,16 @@ You can customize X-Frame-Options with Java Configuration using the following:
|
||||
[source,java]
|
||||
----
|
||||
@Bean
|
||||
SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
|
||||
SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) throws Exception {
|
||||
http
|
||||
// ...
|
||||
.headers()
|
||||
.frameOptions()
|
||||
.mode(SAMEORIGIN);
|
||||
.headers(headers ->
|
||||
headers
|
||||
.frameOptions(frameOptions ->
|
||||
frameOptions
|
||||
.mode(SAMEORIGIN)
|
||||
)
|
||||
);
|
||||
return http.build();
|
||||
}
|
||||
----
|
||||
@@ -264,12 +286,13 @@ However, we can customize with Java Configuration with the following:
|
||||
[source,java]
|
||||
----
|
||||
@Bean
|
||||
SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
|
||||
SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) throws Exception {
|
||||
http
|
||||
// ...
|
||||
.headers()
|
||||
.xssProtection()
|
||||
.disable();
|
||||
.headers(headers ->
|
||||
headers
|
||||
.xssProtection(xssProtection -> xssProtection.disable())
|
||||
);
|
||||
return http.build();
|
||||
}
|
||||
----
|
||||
@@ -345,11 +368,16 @@ You can enable the CSP header using Java configuration as shown below:
|
||||
[source,java]
|
||||
----
|
||||
@Bean
|
||||
SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
|
||||
SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) throws Exception {
|
||||
http
|
||||
// ...
|
||||
.headers()
|
||||
.contentSecurityPolicy("script-src 'self' https://trustedscripts.example.com; object-src https://trustedplugins.example.com; report-uri /csp-report-endpoint/");
|
||||
.headers(headers ->
|
||||
headers
|
||||
.contentSecurityPolicy(contentSecurityPolicy ->
|
||||
contentSecurityPolicy
|
||||
.policyDirectives("script-src 'self' https://trustedscripts.example.com; object-src https://trustedplugins.example.com; report-uri /csp-report-endpoint/")
|
||||
)
|
||||
);
|
||||
return http.build();
|
||||
}
|
||||
----
|
||||
@@ -359,12 +387,17 @@ To enable the CSP _'report-only'_ header, provide the following Java configurati
|
||||
[source,java]
|
||||
----
|
||||
@Bean
|
||||
SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
|
||||
SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) throws Exception {
|
||||
http
|
||||
// ...
|
||||
.headers()
|
||||
.contentSecurityPolicy("script-src 'self' https://trustedscripts.example.com; object-src https://trustedplugins.example.com; report-uri /csp-report-endpoint/")
|
||||
.reportOnly();
|
||||
.headers(headers ->
|
||||
headers
|
||||
.contentSecurityPolicy(contentSecurityPolicy ->
|
||||
contentSecurityPolicy
|
||||
.policyDirectives("script-src 'self' https://trustedscripts.example.com; object-src https://trustedplugins.example.com; report-uri /csp-report-endpoint/")
|
||||
.reportOnly()
|
||||
)
|
||||
);
|
||||
return http.build();
|
||||
}
|
||||
----
|
||||
@@ -405,11 +438,16 @@ You can enable the Referrer-Policy header using Java configuration as shown belo
|
||||
[source,java]
|
||||
----
|
||||
@Bean
|
||||
SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
|
||||
SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) throws Exception {
|
||||
http
|
||||
// ...
|
||||
.headers()
|
||||
.referrerPolicy(ReferrerPolicy.SAME_ORIGIN);
|
||||
.headers(headers ->
|
||||
headers
|
||||
.referrerPolicy(referrerPolicy ->
|
||||
referrerPolicy
|
||||
.policy(ReferrerPolicy.SAME_ORIGIN)
|
||||
)
|
||||
);
|
||||
return http.build();
|
||||
}
|
||||
----
|
||||
@@ -438,11 +476,13 @@ You can enable the Feature-Policy header using Java configuration as shown below
|
||||
[source,java]
|
||||
----
|
||||
@Bean
|
||||
SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
|
||||
SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) throws Exception {
|
||||
http
|
||||
// ...
|
||||
.headers()
|
||||
.featurePolicy("geolocation 'self'");
|
||||
.headers(headers ->
|
||||
headers
|
||||
.featurePolicy("geolocation 'self'")
|
||||
);
|
||||
return http.build();
|
||||
}
|
||||
----
|
||||
|
||||
@@ -88,10 +88,11 @@ public class SecurityConfig {
|
||||
return http
|
||||
// Demonstrate that method security works
|
||||
// Best practice to use both for defense in depth
|
||||
.authorizeExchange()
|
||||
.anyExchange().permitAll()
|
||||
.and()
|
||||
.httpBasic().and()
|
||||
.authorizeExchange(exchanges ->
|
||||
exchanges
|
||||
.anyExchange().permitAll()
|
||||
)
|
||||
.httpBasic(withDefaults())
|
||||
.build();
|
||||
}
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ The next step is to instruct Spring Security that you wish to act as an OAuth2 C
|
||||
SecurityWebFilterChain configure(ServerHttpSecurity http) throws Exception {
|
||||
http
|
||||
// ...
|
||||
.oauth2Client();
|
||||
.oauth2Client(withDefaults());
|
||||
return http.build();
|
||||
}
|
||||
----
|
||||
|
||||
@@ -128,10 +128,10 @@ ReactiveClientRegistrationRepository clientRegistrations() {
|
||||
}
|
||||
|
||||
@Bean
|
||||
SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
|
||||
SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) throws Exception {
|
||||
http
|
||||
// ...
|
||||
.oauth2Login();
|
||||
.oauth2Login(withDefaults());
|
||||
return http.build();
|
||||
}
|
||||
----
|
||||
@@ -141,14 +141,16 @@ Additional configuration options can be seen below:
|
||||
[source,java]
|
||||
----
|
||||
@Bean
|
||||
SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
|
||||
SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) throws Exception {
|
||||
http
|
||||
// ...
|
||||
.oauth2Login()
|
||||
.authenticationConverter(converter)
|
||||
.authenticationManager(manager)
|
||||
.authorizedClientRepository(authorizedClients)
|
||||
.clientRegistrationRepository(clientRegistrations);
|
||||
.oauth2Login(oauth2Login ->
|
||||
oauth2Login
|
||||
.authenticationConverter(converter)
|
||||
.authenticationManager(manager)
|
||||
.authorizedClientRepository(authorizedClients)
|
||||
.clientRegistrationRepository(clientRegistrations)
|
||||
);
|
||||
return http.build();
|
||||
}
|
||||
----
|
||||
|
||||
@@ -121,14 +121,17 @@ The first is a `SecurityWebFilterChain` that configures the app as a resource se
|
||||
[source,java]
|
||||
----
|
||||
@Bean
|
||||
SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
|
||||
http
|
||||
.authorizeExchange()
|
||||
.anyExchange().authenticated()
|
||||
.and()
|
||||
.oauth2ResourceServer()
|
||||
.jwt();
|
||||
return http.build();
|
||||
SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) throws Exception {
|
||||
http
|
||||
.authorizeExchange(exchanges ->
|
||||
exchanges
|
||||
.anyExchange().authenticated()
|
||||
)
|
||||
.oauth2ResourceServer(oauth2ResourceServer ->
|
||||
oauth2ResourceServer
|
||||
.jwt(withDefaults())
|
||||
);
|
||||
return http.build();
|
||||
}
|
||||
----
|
||||
|
||||
@@ -139,14 +142,17 @@ Replacing this is as simple as exposing the bean within the application:
|
||||
[source,java]
|
||||
----
|
||||
@Bean
|
||||
SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
|
||||
SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) throws Exception {
|
||||
http
|
||||
.authorizeExchange()
|
||||
.pathMatchers("/message/**").hasAuthority("SCOPE_message:read")
|
||||
.anyExchange().authenticated()
|
||||
.and()
|
||||
.oauth2ResourceServer()
|
||||
.jwt();
|
||||
.authorizeExchange(exchanges ->
|
||||
exchanges
|
||||
.pathMatchers("/message/**").hasAuthority("SCOPE_message:read")
|
||||
.anyExchange().authenticated()
|
||||
)
|
||||
.oauth2ResourceServer(oauth2ResourceServer ->
|
||||
oauth2ResourceServer
|
||||
.jwt(withDefaults())
|
||||
);
|
||||
return http.build();
|
||||
}
|
||||
----
|
||||
@@ -177,15 +183,20 @@ An authorization server's JWK Set Uri can be configured <<webflux-oauth2-resourc
|
||||
[source,java]
|
||||
----
|
||||
@Bean
|
||||
SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
|
||||
http
|
||||
.authorizeExchange()
|
||||
.anyExchange().authenticated()
|
||||
.and()
|
||||
.oauth2ResourceServer()
|
||||
.jwt()
|
||||
.jwkSetUri("https://idp.example.com/.well-known/jwks.json");
|
||||
return http.build();
|
||||
SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) throws Exception {
|
||||
http
|
||||
.authorizeExchange(exchanges ->
|
||||
exchanges
|
||||
.anyExchange().authenticated()
|
||||
)
|
||||
.oauth2ResourceServer(oauth2ResourceServer ->
|
||||
oauth2ResourceServer
|
||||
.jwt(jwt ->
|
||||
jwt
|
||||
.jwkSetUri("https://idp.example.com/.well-known/jwks.json")
|
||||
)
|
||||
);
|
||||
return http.build();
|
||||
}
|
||||
----
|
||||
|
||||
@@ -199,14 +210,19 @@ More powerful than `jwkSetUri()` is `decoder()`, which will completely replace a
|
||||
[source,java]
|
||||
----
|
||||
@Bean
|
||||
SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
|
||||
http
|
||||
.authorizeExchange()
|
||||
.anyExchange().authenticated()
|
||||
.and()
|
||||
.oauth2ResourceServer()
|
||||
.jwt()
|
||||
.decoder(myCustomDecoder());
|
||||
SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) throws Exception {
|
||||
http
|
||||
.authorizeExchange(exchanges ->
|
||||
exchanges
|
||||
.anyExchange().authenticated()
|
||||
)
|
||||
.oauth2ResourceServer(oauth2ResourceServer ->
|
||||
oauth2ResourceServer
|
||||
.jwt(jwt ->
|
||||
jwt
|
||||
.decoder(myCustomDecoder())
|
||||
)
|
||||
);
|
||||
return http.build();
|
||||
}
|
||||
----
|
||||
@@ -240,15 +256,18 @@ This means that to protect an endpoint or method with a scope derived from a JWT
|
||||
[source,java]
|
||||
----
|
||||
@Bean
|
||||
SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
|
||||
http
|
||||
.authorizeExchange()
|
||||
.mvcMatchers("/contacts/**").hasAuthority("SCOPE_contacts")
|
||||
.mvcMatchers("/messages/**").hasAuthority("SCOPE_messages")
|
||||
.anyExchange().authenticated()
|
||||
.and()
|
||||
.oauth2ResourceServer()
|
||||
.jwt();
|
||||
SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) throws Exception {
|
||||
http
|
||||
.authorizeExchange(exchanges ->
|
||||
exchanges
|
||||
.mvcMatchers("/contacts/**").hasAuthority("SCOPE_contacts")
|
||||
.mvcMatchers("/messages/**").hasAuthority("SCOPE_messages")
|
||||
.anyExchange().authenticated()
|
||||
)
|
||||
.oauth2ResourceServer(oauth2ResourceServer ->
|
||||
oauth2ResourceServer
|
||||
.jwt(withDefaults())
|
||||
);
|
||||
return http.build();
|
||||
}
|
||||
----
|
||||
@@ -273,15 +292,20 @@ To this end, the DSL exposes `jwtAuthenticationConverter()`:
|
||||
[source,java]
|
||||
----
|
||||
@Bean
|
||||
SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
|
||||
http
|
||||
.authorizeExchange()
|
||||
.anyExchange().authenticated()
|
||||
.and()
|
||||
.oauth2ResourceServer()
|
||||
.jwt()
|
||||
.jwtAuthenticationConverter(grantedAuthoritiesExtractor());
|
||||
return http.build();
|
||||
SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) throws Exception {
|
||||
http
|
||||
.authorizeExchange(exchanges ->
|
||||
exchanges
|
||||
.anyExchange().authenticated()
|
||||
)
|
||||
.oauth2ResourceServer(oauth2ResourceServer ->
|
||||
oauth2ResourceServer
|
||||
.jwt(jwt ->
|
||||
jwt
|
||||
.jwtAuthenticationConverter(grantedAuthoritiesExtractor())
|
||||
)
|
||||
);
|
||||
return http.build();
|
||||
}
|
||||
|
||||
Converter<Jwt, Mono<AbstractAuthenticationToken>> grantedAuthoritiesExtractor() {
|
||||
|
||||
@@ -7,10 +7,10 @@ Spring Security can be configured to perform a redirect to https using the follo
|
||||
[source,java]
|
||||
----
|
||||
@Bean
|
||||
SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
|
||||
SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) throws Exception {
|
||||
http
|
||||
// ...
|
||||
.redirectToHttps();
|
||||
.redirectToHttps(withDefaults());
|
||||
return http.build();
|
||||
}
|
||||
----
|
||||
@@ -22,11 +22,13 @@ For example, if the production environment adds a header named `X-Forwarded-Prot
|
||||
[source,java]
|
||||
----
|
||||
@Bean
|
||||
SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
|
||||
SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) throws Exception {
|
||||
http
|
||||
// ...
|
||||
.redirectToHttps()
|
||||
.httpsRedirectWhen(e -> e.getRequest().getHeaders().containsKey("X-Forwarded-Proto"));
|
||||
.redirectToHttps(redirectToHttps ->
|
||||
redirectToHttps
|
||||
.httpsRedirectWhen(e -> e.getRequest().getHeaders().containsKey("X-Forwarded-Proto"))
|
||||
);
|
||||
return http.build();
|
||||
}
|
||||
----
|
||||
|
||||
@@ -52,13 +52,14 @@ public class HelloWebfluxSecurityConfig {
|
||||
}
|
||||
|
||||
@Bean
|
||||
public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
|
||||
public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) throws Exception {
|
||||
http
|
||||
.authorizeExchange()
|
||||
.anyExchange().authenticated()
|
||||
.and()
|
||||
.httpBasic().and()
|
||||
.formLogin();
|
||||
.authorizeExchange(exchanges ->
|
||||
exchanges
|
||||
.anyExchange().authenticated()
|
||||
)
|
||||
.httpBasic(withDefaults())
|
||||
.formLogin(withDefaults());
|
||||
return http.build();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,14 +7,14 @@ Below is an example of a reactive x509 security configuration:
|
||||
[source,java]
|
||||
----
|
||||
@Bean
|
||||
public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
|
||||
http
|
||||
.x509()
|
||||
.and()
|
||||
.authorizeExchange()
|
||||
.anyExchange().permitAll();
|
||||
|
||||
return http.build();
|
||||
public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) throws Exception {
|
||||
http
|
||||
.x509(withDefaults())
|
||||
.authorizeExchange(exchanges ->
|
||||
exchanges
|
||||
.anyExchange().permitAll()
|
||||
);
|
||||
return http.build();
|
||||
}
|
||||
----
|
||||
|
||||
@@ -25,28 +25,28 @@ The next example demonstrates how these defaults can be overridden.
|
||||
[source,java]
|
||||
----
|
||||
@Bean
|
||||
public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
|
||||
SubjectDnX509PrincipalExtractor principalExtractor =
|
||||
new SubjectDnX509PrincipalExtractor();
|
||||
public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) throws Exception {
|
||||
SubjectDnX509PrincipalExtractor principalExtractor =
|
||||
new SubjectDnX509PrincipalExtractor();
|
||||
|
||||
principalExtractor.setSubjectDnRegex("OU=(.*?)(?:,|$)");
|
||||
principalExtractor.setSubjectDnRegex("OU=(.*?)(?:,|$)");
|
||||
|
||||
ReactiveAuthenticationManager authenticationManager = authentication -> {
|
||||
authentication.setAuthenticated("Trusted Org Unit".equals(authentication.getName()));
|
||||
return Mono.just(authentication);
|
||||
};
|
||||
ReactiveAuthenticationManager authenticationManager = authentication -> {
|
||||
authentication.setAuthenticated("Trusted Org Unit".equals(authentication.getName()));
|
||||
return Mono.just(authentication);
|
||||
};
|
||||
|
||||
// @formatter:off
|
||||
http
|
||||
.x509()
|
||||
.principalExtractor(principalExtractor)
|
||||
.authenticationManager(authenticationManager)
|
||||
.and()
|
||||
.authorizeExchange()
|
||||
.anyExchange().authenticated();
|
||||
// @formatter:on
|
||||
|
||||
return http.build();
|
||||
http
|
||||
.x509(x509 ->
|
||||
x509
|
||||
.principalExtractor(principalExtractor)
|
||||
.authenticationManager(authenticationManager)
|
||||
)
|
||||
.authorizeExchange(exchanges ->
|
||||
exchanges
|
||||
.anyExchange().authenticated()
|
||||
);
|
||||
return http.build();
|
||||
}
|
||||
----
|
||||
|
||||
|
||||
Reference in New Issue
Block a user