diff --git a/config/src/main/java/org/springframework/security/config/web/server/ServerHttpSecurity.java b/config/src/main/java/org/springframework/security/config/web/server/ServerHttpSecurity.java
index 6d89dadc24..a8b21aaa8d 100644
--- a/config/src/main/java/org/springframework/security/config/web/server/ServerHttpSecurity.java
+++ b/config/src/main/java/org/springframework/security/config/web/server/ServerHttpSecurity.java
@@ -32,6 +32,7 @@ import java.util.UUID;
import java.util.function.Function;
import java.util.function.Supplier;
+import org.springframework.security.config.Customizer;
import reactor.core.publisher.Mono;
import reactor.util.context.Context;
@@ -394,6 +395,48 @@ public class ServerHttpSecurity {
return this.httpsRedirectSpec;
}
+ /**
+ * Configures HTTPS redirection rules. If the default is used:
+ *
+ *
+ * @Bean
+ * public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
+ * http
+ * // ...
+ * .redirectToHttps(withDefaults());
+ * return http.build();
+ * }
+ *
+ *
+ * Then all non-HTTPS requests will be redirected to HTTPS.
+ *
+ * Typically, all requests should be HTTPS; however, the focus for redirection can also be narrowed:
+ *
+ *
+ * @Bean
+ * public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) throws Exception {
+ * http
+ * // ...
+ * .redirectToHttps(redirectToHttps ->
+ * redirectToHttps
+ * .httpsRedirectWhen(serverWebExchange ->
+ * serverWebExchange.getRequest().getHeaders().containsKey("X-Requires-Https"))
+ * );
+ * return http.build();
+ * }
+ *
+ *
+ * @param httpsRedirectCustomizer the {@link Customizer} to provide more options for
+ * the {@link HttpsRedirectSpec}
+ * @return the {@link ServerHttpSecurity} to customize
+ * @throws Exception
+ */
+ public ServerHttpSecurity redirectToHttps(Customizer httpsRedirectCustomizer) throws Exception {
+ this.httpsRedirectSpec = new HttpsRedirectSpec();
+ httpsRedirectCustomizer.customize(this.httpsRedirectSpec);
+ return this;
+ }
+
/**
* Configures CSRF Protection
* which is enabled by default. You can disable it using:
@@ -436,6 +479,56 @@ public class ServerHttpSecurity {
return this.csrf;
}
+ /**
+ * Configures CSRF Protection
+ * which is enabled by default. You can disable it using:
+ *
+ *
+ * @Bean
+ * public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) throws Exception {
+ * http
+ * // ...
+ * .csrf(csrf ->
+ * csrf.disabled()
+ * );
+ * return http.build();
+ * }
+ *
+ *
+ * Additional configuration options can be seen below:
+ *
+ *
+ *
+ * @Bean
+ * public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) throws Exception {
+ * http
+ * // ...
+ * .csrf(csrf ->
+ * csrf
+ * // Handle CSRF failures
+ * .accessDeniedHandler(accessDeniedHandler)
+ * // Custom persistence of CSRF Token
+ * .csrfTokenRepository(csrfTokenRepository)
+ * // custom matching when CSRF protection is enabled
+ * .requireCsrfProtectionMatcher(matcher)
+ * );
+ * return http.build();
+ * }
+ *
+ *
+ * @param csrfCustomizer the {@link Customizer} to provide more options for
+ * the {@link CsrfSpec}
+ * @return the {@link ServerHttpSecurity} to customize
+ * @throws Exception
+ */
+ public ServerHttpSecurity csrf(Customizer csrfCustomizer) throws Exception {
+ if (this.csrf == null) {
+ this.csrf = new CsrfSpec();
+ }
+ csrfCustomizer.customize(this.csrf);
+ return this;
+ }
+
/**
* Configures CORS headers. By default if a {@link CorsConfigurationSource} Bean is found, it will be used
* to create a {@link CorsWebFilter}. If {@link CorsSpec#configurationSource(CorsConfigurationSource)} is invoked
@@ -449,6 +542,24 @@ public class ServerHttpSecurity {
return this.cors;
}
+ /**
+ * Configures CORS headers. By default if a {@link CorsConfigurationSource} Bean is found, it will be used
+ * to create a {@link CorsWebFilter}. If {@link CorsSpec#configurationSource(CorsConfigurationSource)} is invoked
+ * it will be used instead. If neither has been configured, the Cors configuration will do nothing.
+ *
+ * @param corsCustomizer the {@link Customizer} to provide more options for
+ * the {@link CorsSpec}
+ * @return the {@link ServerHttpSecurity} to customize
+ * @throws Exception
+ */
+ public ServerHttpSecurity cors(Customizer corsCustomizer) throws Exception {
+ if (this.cors == null) {
+ this.cors = new CorsSpec();
+ }
+ corsCustomizer.customize(this.cors);
+ return this;
+ }
+
/**
* Enables and Configures anonymous authentication. Anonymous Authentication is disabled by default.
*
@@ -473,6 +584,36 @@ public class ServerHttpSecurity {
return this.anonymous;
}
+ /**
+ * Enables and Configures anonymous authentication. Anonymous Authentication is disabled by default.
+ *
+ *
+ * @Bean
+ * public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) throws Exception {
+ * http
+ * // ...
+ * .anonymous(anonymous ->
+ * anonymous
+ * .key("key")
+ * .authorities("ROLE_ANONYMOUS")
+ * );
+ * return http.build();
+ * }
+ *
+ *
+ * @param anonymousCustomizer the {@link Customizer} to provide more options for
+ * the {@link AnonymousSpec}
+ * @return the {@link ServerHttpSecurity} to customize
+ * @throws Exception
+ */
+ public ServerHttpSecurity anonymous(Customizer anonymousCustomizer) throws Exception {
+ if (this.anonymous == null) {
+ this.anonymous = new AnonymousSpec();
+ }
+ anonymousCustomizer.customize(this.anonymous);
+ return this;
+ }
+
/**
* Configures CORS support within Spring Security. This ensures that the {@link CorsWebFilter} is place in the
* correct order.
@@ -560,6 +701,38 @@ public class ServerHttpSecurity {
return this.httpBasic;
}
+ /**
+ * Configures HTTP Basic authentication. An example configuration is provided below:
+ *
+ *
+ * @Bean
+ * public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) throws Exception {
+ * http
+ * // ...
+ * .httpBasic(httpBasic ->
+ * httpBasic
+ * // used for authenticating the credentials
+ * .authenticationManager(authenticationManager)
+ * // Custom persistence of the authentication
+ * .securityContextRepository(securityContextRepository)
+ * );
+ * return http.build();
+ * }
+ *
+ *
+ * @param httpBasicCustomizer the {@link Customizer} to provide more options for
+ * the {@link HttpBasicSpec}
+ * @return the {@link ServerHttpSecurity} to customize
+ * @throws Exception
+ */
+ public ServerHttpSecurity httpBasic(Customizer httpBasicCustomizer) throws Exception {
+ if (this.httpBasic == null) {
+ this.httpBasic = new HttpBasicSpec();
+ }
+ httpBasicCustomizer.customize(this.httpBasic);
+ return this;
+ }
+
/**
* Configures form based authentication. An example configuration is provided below:
*
@@ -590,6 +763,42 @@ public class ServerHttpSecurity {
return this.formLogin;
}
+ /**
+ * Configures form based authentication. An example configuration is provided below:
+ *
+ *
+ * @Bean
+ * public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) throws Exception {
+ * http
+ * // ...
+ * .formLogin(formLogin ->
+ * formLogin
+ * // used for authenticating the credentials
+ * .authenticationManager(authenticationManager)
+ * // Custom persistence of the authentication
+ * .securityContextRepository(securityContextRepository)
+ * // expect a log in page at "/authenticate"
+ * // a POST "/authenticate" is where authentication occurs
+ * // error page at "/authenticate?error"
+ * .loginPage("/authenticate")
+ * );
+ * return http.build();
+ * }
+ *
+ *
+ * @param formLoginCustomizer the {@link Customizer} to provide more options for
+ * the {@link FormLoginSpec}
+ * @return the {@link ServerHttpSecurity} to customize
+ * @throws Exception
+ */
+ public ServerHttpSecurity formLogin(Customizer formLoginCustomizer) throws Exception {
+ if (this.formLogin == null) {
+ this.formLogin = new FormLoginSpec();
+ }
+ formLoginCustomizer.customize(this.formLogin);
+ return this;
+ }
+
/**
* Configures x509 authentication using a certificate provided by a client.
*
@@ -619,6 +828,39 @@ public class ServerHttpSecurity {
return this.x509;
}
+ /**
+ * Configures x509 authentication using a certificate provided by a client.
+ *
+ *
+ * @Bean
+ * public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) throws Exception {
+ * http
+ * .x509(x509 ->
+ * x509
+ * .authenticationManager(authenticationManager)
+ * .principalExtractor(principalExtractor)
+ * );
+ * return http.build();
+ * }
+ *
+ *
+ * Note that if extractor is not specified, {@link SubjectDnX509PrincipalExtractor} will be used.
+ * If authenticationManager is not specified, {@link ReactivePreAuthenticatedAuthenticationManager} will be used.
+ *
+ * @since 5.2
+ * @param x509Customizer the {@link Customizer} to provide more options for
+ * the {@link X509Spec}
+ * @return the {@link ServerHttpSecurity} to customize
+ * @throws Exception
+ */
+ public ServerHttpSecurity x509(Customizer x509Customizer) throws Exception {
+ if (this.x509 == null) {
+ this.x509 = new X509Spec();
+ }
+ x509Customizer.customize(this.x509);
+ return this;
+ }
+
/**
* Configures X509 authentication
*
@@ -702,6 +944,36 @@ public class ServerHttpSecurity {
return this.oauth2Login;
}
+ /**
+ * Configures authentication support using an OAuth 2.0 and/or OpenID Connect 1.0 Provider.
+ *
+ *
+ * @Bean
+ * public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) throws Exception {
+ * http
+ * // ...
+ * .oauth2Login(oauth2Login ->
+ * oauth2Login
+ * .authenticationConverter(authenticationConverter)
+ * .authenticationManager(manager)
+ * );
+ * return http.build();
+ * }
+ *
+ *
+ * @param oauth2LoginCustomizer the {@link Customizer} to provide more options for
+ * the {@link OAuth2LoginSpec}
+ * @return the {@link ServerHttpSecurity} to customize
+ * @throws Exception
+ */
+ public ServerHttpSecurity oauth2Login(Customizer oauth2LoginCustomizer) throws Exception {
+ if (this.oauth2Login == null) {
+ this.oauth2Login = new OAuth2LoginSpec();
+ }
+ oauth2LoginCustomizer.customize(this.oauth2Login);
+ return this;
+ }
+
public class OAuth2LoginSpec {
private ReactiveClientRegistrationRepository clientRegistrationRepository;
@@ -995,6 +1267,36 @@ public class ServerHttpSecurity {
return this.client;
}
+ /**
+ * Configures the OAuth2 client.
+ *
+ *
+ * @Bean
+ * public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) throws Exception {
+ * http
+ * // ...
+ * .oauth2Client(oauth2Client ->
+ * oauth2Client
+ * .clientRegistrationRepository(clientRegistrationRepository)
+ * .authorizedClientRepository(authorizedClientRepository)
+ * );
+ * return http.build();
+ * }
+ *
+ *
+ * @param oauth2ClientCustomizer the {@link Customizer} to provide more options for
+ * the {@link OAuth2ClientSpec}
+ * @return the {@link ServerHttpSecurity} to customize
+ * @throws Exception
+ */
+ public ServerHttpSecurity oauth2Client(Customizer oauth2ClientCustomizer) throws Exception {
+ if (this.client == null) {
+ this.client = new OAuth2ClientSpec();
+ }
+ oauth2ClientCustomizer.customize(this.client);
+ return this;
+ }
+
public class OAuth2ClientSpec {
private ReactiveClientRegistrationRepository clientRegistrationRepository;
@@ -1145,6 +1447,39 @@ public class ServerHttpSecurity {
return this.resourceServer;
}
+ /**
+ * Configures OAuth 2.0 Resource Server support.
+ *
+ *
+ * @Bean
+ * public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) throws Exception {
+ * http
+ * // ...
+ * .oauth2ResourceServer(oauth2ResourceServer ->
+ * oauth2ResourceServer
+ * .jwt(jwt ->
+ * jwt
+ * .publicKey(publicKey())
+ * )
+ * );
+ * return http.build();
+ * }
+ *
+ *
+ * @param oauth2ResourceServerCustomizer the {@link Customizer} to provide more options for
+ * the {@link OAuth2ResourceServerSpec}
+ * @return the {@link ServerHttpSecurity} to customize
+ * @throws Exception
+ */
+ public ServerHttpSecurity oauth2ResourceServer(Customizer oauth2ResourceServerCustomizer)
+ throws Exception {
+ if (this.resourceServer == null) {
+ this.resourceServer = new OAuth2ResourceServerSpec();
+ }
+ oauth2ResourceServerCustomizer.customize(this.resourceServer);
+ return this;
+ }
+
/**
* Configures OAuth2 Resource Server Support
*/
@@ -1228,6 +1563,22 @@ public class ServerHttpSecurity {
return this.jwt;
}
+ /**
+ * Enables JWT Resource Server support.
+ *
+ * @param jwtCustomizer the {@link Customizer} to provide more options for
+ * the {@link JwtSpec}
+ * @return the {@link OAuth2ResourceServerSpec} to customize
+ * @throws Exception
+ */
+ public OAuth2ResourceServerSpec jwt(Customizer jwtCustomizer) throws Exception {
+ if (this.jwt == null) {
+ this.jwt = new JwtSpec();
+ }
+ jwtCustomizer.customize(this.jwt);
+ return this;
+ }
+
/**
* Enables Opaque Token Resource Server support.
*
@@ -1240,6 +1591,22 @@ public class ServerHttpSecurity {
return this.opaqueToken;
}
+ /**
+ * Enables Opaque Token Resource Server support.
+ *
+ * @param opaqueTokenCustomizer the {@link Customizer} to provide more options for
+ * the {@link OpaqueTokenSpec}
+ * @return the {@link OAuth2ResourceServerSpec} to customize
+ * @throws Exception
+ */
+ public OAuth2ResourceServerSpec opaqueToken(Customizer opaqueTokenCustomizer) throws Exception {
+ if (this.opaqueToken == null) {
+ this.opaqueToken = new OpaqueTokenSpec();
+ }
+ opaqueTokenCustomizer.customize(this.opaqueToken);
+ return this;
+ }
+
protected void configure(ServerHttpSecurity http) {
this.bearerTokenServerWebExchangeMatcher
.setBearerTokenConverter(this.bearerTokenConverter);
@@ -1561,6 +1928,58 @@ public class ServerHttpSecurity {
return this.headers;
}
+ /**
+ * Configures HTTP Response Headers. The default headers are:
+ *
+ *
+ * Cache-Control: no-cache, no-store, max-age=0, must-revalidate
+ * Pragma: no-cache
+ * Expires: 0
+ * X-Content-Type-Options: nosniff
+ * Strict-Transport-Security: max-age=31536000 ; includeSubDomains
+ * X-Frame-Options: DENY
+ * X-XSS-Protection: 1; mode=block
+ *
+ *
+ * such that "Strict-Transport-Security" is only added on secure requests.
+ *
+ * An example configuration is provided below:
+ *
+ *
+ * @Bean
+ * public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) throws Exception {
+ * http
+ * // ...
+ * .headers(headers ->
+ * headers
+ * // customize frame options to be same origin
+ * .frameOptions(frameOptions ->
+ * frameOptions
+ * .mode(XFrameOptionsServerHttpHeadersWriter.Mode.SAMEORIGIN)
+ * )
+ * // disable cache control
+ * .cache(cache ->
+ * cache
+ * .disable()
+ * )
+ * );
+ * return http.build();
+ * }
+ *
+ *
+ * @param headerCustomizer the {@link Customizer} to provide more options for
+ * the {@link HeaderSpec}
+ * @return the {@link ServerHttpSecurity} to customize
+ * @throws Exception
+ */
+ public ServerHttpSecurity headers(Customizer headerCustomizer) throws Exception {
+ if (this.headers == null) {
+ this.headers = new HeaderSpec();
+ }
+ headerCustomizer.customize(this.headers);
+ return this;
+ }
+
/**
* Configures exception handling (i.e. handles when authentication is requested). An example configuration can
* be found below:
@@ -1586,6 +2005,38 @@ public class ServerHttpSecurity {
return this.exceptionHandling;
}
+ /**
+ * Configures exception handling (i.e. handles when authentication is requested). An example configuration can
+ * be found below:
+ *
+ *
+ * @Bean
+ * public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) throws Exception {
+ * http
+ * // ...
+ * .exceptionHandling(exceptionHandling ->
+ * exceptionHandling
+ * // customize how to request for authentication
+ * .authenticationEntryPoint(entryPoint)
+ * );
+ * return http.build();
+ * }
+ *
+ *
+ * @param exceptionHandlingCustomizer the {@link Customizer} to provide more options for
+ * the {@link ExceptionHandlingSpec}
+ * @return the {@link ServerHttpSecurity} to customize
+ * @throws Exception
+ */
+ public ServerHttpSecurity exceptionHandling(Customizer exceptionHandlingCustomizer)
+ throws Exception {
+ if (this.exceptionHandling == null) {
+ this.exceptionHandling = new ExceptionHandlingSpec();
+ }
+ exceptionHandlingCustomizer.customize(this.exceptionHandling);
+ return this;
+ }
+
/**
* Configures authorization. An example configuration can be found below:
*
@@ -1624,6 +2075,51 @@ public class ServerHttpSecurity {
return this.authorizeExchange;
}
+ /**
+ * Configures authorization. An example configuration can be found below:
+ *
+ *
+ * @Bean
+ * public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) throws Exception {
+ * http
+ * // ...
+ * .authorizeExchange(exchanges ->
+ * exchanges
+ * // any URL that starts with /admin/ requires the role "ROLE_ADMIN"
+ * .pathMatchers("/admin/**").hasRole("ADMIN")
+ * // a POST to /users requires the role "USER_POST"
+ * .pathMatchers(HttpMethod.POST, "/users").hasAuthority("USER_POST")
+ * // a request to /users/{username} requires the current authentication's username
+ * // to be equal to the {username}
+ * .pathMatchers("/users/{username}").access((authentication, context) ->
+ * authentication
+ * .map(Authentication::getName)
+ * .map(username -> username.equals(context.getVariables().get("username")))
+ * .map(AuthorizationDecision::new)
+ * )
+ * // allows providing a custom matching strategy that requires the role "ROLE_CUSTOM"
+ * .matchers(customMatcher).hasRole("CUSTOM")
+ * // any other request requires the user to be authenticated
+ * .anyExchange().authenticated()
+ * );
+ * return http.build();
+ * }
+ *
+ *
+ * @param authorizeExchangeCustomizer the {@link Customizer} to provide more options for
+ * the {@link AuthorizeExchangeSpec}
+ * @return the {@link ServerHttpSecurity} to customize
+ * @throws Exception
+ */
+ public ServerHttpSecurity authorizeExchange(Customizer authorizeExchangeCustomizer)
+ throws Exception {
+ if (this.authorizeExchange == null) {
+ this.authorizeExchange = new AuthorizeExchangeSpec();
+ }
+ authorizeExchangeCustomizer.customize(this.authorizeExchange);
+ return this;
+ }
+
/**
* Configures log out. An example configuration can be found below:
*
@@ -1651,6 +2147,40 @@ public class ServerHttpSecurity {
return this.logout;
}
+ /**
+ * Configures log out. An example configuration can be found below:
+ *
+ *
+ * @Bean
+ * public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) throws Exception {
+ * http
+ * // ...
+ * .logout(logout ->
+ * logout
+ * // configures how log out is done
+ * .logoutHandler(logoutHandler)
+ * // log out will be performed on POST /signout
+ * .logoutUrl("/signout")
+ * // configure what is done on logout success
+ * .logoutSuccessHandler(successHandler)
+ * );
+ * return http.build();
+ * }
+ *
+ *
+ * @param logoutCustomizer the {@link Customizer} to provide more options for
+ * the {@link LogoutSpec}
+ * @return the {@link ServerHttpSecurity} to customize
+ * @throws Exception
+ */
+ public ServerHttpSecurity logout(Customizer logoutCustomizer) throws Exception {
+ if (this.logout == null) {
+ this.logout = new LogoutSpec();
+ }
+ logoutCustomizer.customize(this.logout);
+ return this;
+ }
+
/**
* Configures the request cache which is used when a flow is interrupted (i.e. due to requesting credentials) so
* that the request can be replayed after authentication. An example configuration can be found below:
@@ -1673,6 +2203,34 @@ public class ServerHttpSecurity {
return this.requestCache;
}
+ /**
+ * Configures the request cache which is used when a flow is interrupted (i.e. due to requesting credentials) so
+ * that the request can be replayed after authentication. An example configuration can be found below:
+ *
+ *
+ * @Bean
+ * public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) throws Exception {
+ * http
+ * // ...
+ * .requestCache(requestCache ->
+ * requestCache
+ * // configures how the request is cached
+ * .requestCache(customRequestCache)
+ * );
+ * return http.build();
+ * }
+ *
+ *
+ * @param requestCacheCustomizer the {@link Customizer} to provide more options for
+ * the {@link RequestCacheSpec}
+ * @return the {@link ServerHttpSecurity} to customize
+ * @throws Exception
+ */
+ public ServerHttpSecurity requestCache(Customizer requestCacheCustomizer) throws Exception {
+ requestCacheCustomizer.customize(this.requestCache);
+ return this;
+ }
+
/**
* Configure the default authentication manager.
* @param manager the authentication manager to use
@@ -2549,6 +3107,19 @@ public class ServerHttpSecurity {
return new CacheSpec();
}
+ /**
+ * Configures cache control headers
+ *
+ * @param cacheCustomizer the {@link Customizer} to provide more options for
+ * the {@link CacheSpec}
+ * @return the {@link HeaderSpec} to customize
+ * @throws Exception
+ */
+ public HeaderSpec cache(Customizer cacheCustomizer) throws Exception {
+ cacheCustomizer.customize(new CacheSpec());
+ return this;
+ }
+
/**
* Configures content type response headers
* @return the {@link ContentTypeOptionsSpec} to configure
@@ -2557,6 +3128,20 @@ public class ServerHttpSecurity {
return new ContentTypeOptionsSpec();
}
+ /**
+ * Configures content type response headers
+ *
+ * @param contentTypeOptionsCustomizer the {@link Customizer} to provide more options for
+ * the {@link ContentTypeOptionsSpec}
+ * @return the {@link HeaderSpec} to customize
+ * @throws Exception
+ */
+ public HeaderSpec contentTypeOptions(Customizer contentTypeOptionsCustomizer)
+ throws Exception {
+ contentTypeOptionsCustomizer.customize(new ContentTypeOptionsSpec());
+ return this;
+ }
+
/**
* Configures frame options response headers
* @return the {@link FrameOptionsSpec} to configure
@@ -2565,6 +3150,19 @@ public class ServerHttpSecurity {
return new FrameOptionsSpec();
}
+ /**
+ * Configures frame options response headers
+ *
+ * @param frameOptionsCustomizer the {@link Customizer} to provide more options for
+ * the {@link FrameOptionsSpec}
+ * @return the {@link HeaderSpec} to customize
+ * @throws Exception
+ */
+ public HeaderSpec frameOptions(Customizer frameOptionsCustomizer) throws Exception {
+ frameOptionsCustomizer.customize(new FrameOptionsSpec());
+ return this;
+ }
+
/**
* Configures the Strict Transport Security response headers
* @return the {@link HstsSpec} to configure
@@ -2573,6 +3171,19 @@ public class ServerHttpSecurity {
return new HstsSpec();
}
+ /**
+ * Configures the Strict Transport Security response headers
+ *
+ * @param hstsCustomizer the {@link Customizer} to provide more options for
+ * the {@link HstsSpec}
+ * @return the {@link HeaderSpec} to customize
+ * @throws Exception
+ */
+ public HeaderSpec hsts(Customizer hstsCustomizer) throws Exception {
+ hstsCustomizer.customize(new HstsSpec());
+ return this;
+ }
+
protected void configure(ServerHttpSecurity http) {
ServerHttpHeadersWriter writer = new CompositeServerHttpHeadersWriter(this.writers);
HttpHeaderWriterWebFilter result = new HttpHeaderWriterWebFilter(writer);
@@ -2587,6 +3198,19 @@ public class ServerHttpSecurity {
return new XssProtectionSpec();
}
+ /**
+ * Configures x-xss-protection response header.
+ *
+ * @param xssProtectionCustomizer the {@link Customizer} to provide more options for
+ * the {@link XssProtectionSpec}
+ * @return the {@link HeaderSpec} to customize
+ * @throws Exception
+ */
+ public HeaderSpec xssProtection(Customizer xssProtectionCustomizer) throws Exception {
+ xssProtectionCustomizer.customize(new XssProtectionSpec());
+ return this;
+ }
+
/**
* Configures {@code Content-Security-Policy} response header.
* @param policyDirectives the policy directive(s)
@@ -2596,6 +3220,20 @@ public class ServerHttpSecurity {
return new ContentSecurityPolicySpec(policyDirectives);
}
+ /**
+ * Configures {@code Content-Security-Policy} response header.
+ *
+ * @param contentSecurityPolicyCustomizer the {@link Customizer} to provide more options for
+ * the {@link ContentSecurityPolicySpec}
+ * @return the {@link HeaderSpec} to customize
+ * @throws Exception
+ */
+ public HeaderSpec contentSecurityPolicy(Customizer contentSecurityPolicyCustomizer)
+ throws Exception {
+ contentSecurityPolicyCustomizer.customize(new ContentSecurityPolicySpec());
+ return this;
+ }
+
/**
* Configures {@code Feature-Policy} response header.
* @param policyDirectives the policy directive(s)
@@ -2622,6 +3260,20 @@ public class ServerHttpSecurity {
return new ReferrerPolicySpec();
}
+ /**
+ * Configures {@code Referrer-Policy} response header.
+ *
+ * @param referrerPolicyCustomizer the {@link Customizer} to provide more options for
+ * the {@link ReferrerPolicySpec}
+ * @return the {@link HeaderSpec} to customize
+ * @throws Exception
+ */
+ public HeaderSpec referrerPolicy(Customizer referrerPolicyCustomizer)
+ throws Exception {
+ referrerPolicyCustomizer.customize(new ReferrerPolicySpec());
+ return this;
+ }
+
/**
* Configures cache control headers
* @see #cache()
@@ -2781,6 +3433,7 @@ public class ServerHttpSecurity {
* @since 5.1
*/
public class ContentSecurityPolicySpec {
+ private static final String DEFAULT_SRC_SELF_POLICY = "default-src 'self'";
/**
* Whether to include the {@code Content-Security-Policy-Report-Only} header in
@@ -2793,6 +3446,17 @@ public class ServerHttpSecurity {
return HeaderSpec.this;
}
+ /**
+ * Sets the security policy directive(s) to be used in the response header.
+ *
+ * @param policyDirectives the security policy directive(s)
+ * @return the {@link HeaderSpec} to continue configuring
+ */
+ public HeaderSpec policyDirectives(String policyDirectives) {
+ HeaderSpec.this.contentSecurityPolicy.setPolicyDirectives(policyDirectives);
+ return HeaderSpec.this;
+ }
+
/**
* Allows method chaining to continue configuring the
* {@link ServerHttpSecurity}.
@@ -2806,6 +3470,9 @@ public class ServerHttpSecurity {
HeaderSpec.this.contentSecurityPolicy.setPolicyDirectives(policyDirectives);
}
+ private ContentSecurityPolicySpec() {
+ HeaderSpec.this.contentSecurityPolicy.setPolicyDirectives(DEFAULT_SRC_SELF_POLICY);
+ }
}
/**
@@ -2840,6 +3507,17 @@ public class ServerHttpSecurity {
*/
public class ReferrerPolicySpec {
+ /**
+ * Sets the policy to be used in the response header.
+ *
+ * @param referrerPolicy a referrer policy
+ * @return the {@link ReferrerPolicySpec} to continue configuring
+ */
+ public ReferrerPolicySpec policy(ReferrerPolicy referrerPolicy) {
+ HeaderSpec.this.referrerPolicy.setPolicy(referrerPolicy);
+ return this;
+ }
+
/**
* Allows method chaining to continue configuring the
* {@link ServerHttpSecurity}.
diff --git a/config/src/test/java/org/springframework/security/config/web/server/AuthorizeExchangeSpecTests.java b/config/src/test/java/org/springframework/security/config/web/server/AuthorizeExchangeSpecTests.java
index a645d2b70e..bfc9b3a077 100644
--- a/config/src/test/java/org/springframework/security/config/web/server/AuthorizeExchangeSpecTests.java
+++ b/config/src/test/java/org/springframework/security/config/web/server/AuthorizeExchangeSpecTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2017 the original author or authors.
+ * Copyright 2002-2019 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.
@@ -92,6 +92,39 @@ public class AuthorizeExchangeSpecTests {
.expectStatus().isUnauthorized();
}
+ @Test
+ public void antMatchersWhenPatternsInLambdaThenAnyMethod() throws Exception {
+ this.http
+ .csrf(ServerHttpSecurity.CsrfSpec::disable)
+ .authorizeExchange(exchanges ->
+ exchanges
+ .pathMatchers("/a", "/b").denyAll()
+ .anyExchange().permitAll()
+ );
+
+ WebTestClient client = buildClient();
+
+ client.get()
+ .uri("/a")
+ .exchange()
+ .expectStatus().isUnauthorized();
+
+ client.get()
+ .uri("/b")
+ .exchange()
+ .expectStatus().isUnauthorized();
+
+ client.post()
+ .uri("/a")
+ .exchange()
+ .expectStatus().isUnauthorized();
+
+ client.post()
+ .uri("/b")
+ .exchange()
+ .expectStatus().isUnauthorized();
+ }
+
@Test(expected = IllegalStateException.class)
public void antMatchersWhenNoAccessAndAnotherMatcherThenThrowsException() {
this.http
@@ -117,6 +150,16 @@ public class AuthorizeExchangeSpecTests {
this.http.build();
}
+ @Test(expected = IllegalStateException.class)
+ public void buildWhenMatcherDefinedWithNoAccessInLambdaThenThrowsException() throws Exception {
+ this.http
+ .authorizeExchange(exchanges ->
+ exchanges
+ .pathMatchers("/incomplete")
+ );
+ this.http.build();
+ }
+
private WebTestClient buildClient() {
return WebTestClientBuilder.bindToWebFilters(this.http.build()).build();
}
diff --git a/config/src/test/java/org/springframework/security/config/web/server/CorsSpecTests.java b/config/src/test/java/org/springframework/security/config/web/server/CorsSpecTests.java
index 45dc46c29b..7689f539cb 100644
--- a/config/src/test/java/org/springframework/security/config/web/server/CorsSpecTests.java
+++ b/config/src/test/java/org/springframework/security/config/web/server/CorsSpecTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2017 the original author or authors.
+ * Copyright 2002-2019 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.
@@ -74,6 +74,14 @@ public class CorsSpecTests {
assertHeaders();
}
+ @Test
+ public void corsWhenEnabledInLambdaThenAccessControlAllowOriginAndSecurityHeaders() throws Exception {
+ this.http.cors(cors -> cors.configurationSource(this.source));
+ this.expectedHeaders.set("Access-Control-Allow-Origin", "*");
+ this.expectedHeaders.set("X-Frame-Options", "DENY");
+ assertHeaders();
+ }
+
@Test
public void corsWhenCorsConfigurationSourceBeanThenAccessControlAllowOriginAndSecurityHeaders() {
when(this.context.getBeanNamesForType(any(ResolvableType.class))).thenReturn(new String[] {"source"}, new String[0]);
diff --git a/config/src/test/java/org/springframework/security/config/web/server/ExceptionHandlingSpecTests.java b/config/src/test/java/org/springframework/security/config/web/server/ExceptionHandlingSpecTests.java
index 8421504cbb..1b0dcce0e1 100644
--- a/config/src/test/java/org/springframework/security/config/web/server/ExceptionHandlingSpecTests.java
+++ b/config/src/test/java/org/springframework/security/config/web/server/ExceptionHandlingSpecTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2018 the original author or authors.
+ * Copyright 2002-2019 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.
@@ -26,6 +26,8 @@ import org.springframework.security.web.server.authorization.HttpStatusServerAcc
import org.springframework.security.web.server.authorization.ServerAccessDeniedHandler;
import org.springframework.test.web.reactive.server.WebTestClient;
+import static org.springframework.security.config.Customizer.withDefaults;
+
/**
* @author Denys Ivano
* @since 5.0.5
@@ -56,6 +58,29 @@ public class ExceptionHandlingSpecTests {
.expectHeader().valueMatches("WWW-Authenticate", "Basic.*");
}
+ @Test
+ public void requestWhenExceptionHandlingWithDefaultsInLambdaThenDefaultAuthenticationEntryPointUsed()
+ throws Exception {
+ SecurityWebFilterChain securityWebFilter = this.http
+ .authorizeExchange(exchanges ->
+ exchanges
+ .anyExchange().authenticated()
+ )
+ .exceptionHandling(withDefaults())
+ .build();
+
+ WebTestClient client = WebTestClientBuilder
+ .bindToWebFilters(securityWebFilter)
+ .build();
+
+ client
+ .get()
+ .uri("/test")
+ .exchange()
+ .expectStatus().isUnauthorized()
+ .expectHeader().valueMatches("WWW-Authenticate", "Basic.*");
+ }
+
@Test
public void customAuthenticationEntryPoint() {
SecurityWebFilterChain securityWebFilter = this.http
@@ -80,6 +105,31 @@ public class ExceptionHandlingSpecTests {
.expectHeader().valueMatches("Location", ".*");
}
+ @Test
+ public void requestWhenCustomAuthenticationEntryPointInLambdaThenCustomAuthenticationEntryPointUsed() throws Exception {
+ SecurityWebFilterChain securityWebFilter = this.http
+ .authorizeExchange(exchanges ->
+ exchanges
+ .anyExchange().authenticated()
+ )
+ .exceptionHandling(exceptionHandling ->
+ exceptionHandling
+ .authenticationEntryPoint(redirectServerAuthenticationEntryPoint("/auth"))
+ )
+ .build();
+
+ WebTestClient client = WebTestClientBuilder
+ .bindToWebFilters(securityWebFilter)
+ .build();
+
+ client
+ .get()
+ .uri("/test")
+ .exchange()
+ .expectStatus().isFound()
+ .expectHeader().valueMatches("Location", ".*");
+ }
+
@Test
public void defaultAccessDeniedHandler() {
SecurityWebFilterChain securityWebFilter = this.http
@@ -104,6 +154,30 @@ public class ExceptionHandlingSpecTests {
.expectStatus().isForbidden();
}
+ @Test
+ public void requestWhenExceptionHandlingWithDefaultsInLambdaThenDefaultAccessDeniedHandlerUsed()
+ throws Exception {
+ SecurityWebFilterChain securityWebFilter = this.http
+ .httpBasic(withDefaults())
+ .authorizeExchange(exchanges ->
+ exchanges
+ .anyExchange().hasRole("ADMIN")
+ )
+ .exceptionHandling(withDefaults())
+ .build();
+
+ WebTestClient client = WebTestClientBuilder
+ .bindToWebFilters(securityWebFilter)
+ .build();
+
+ client
+ .get()
+ .uri("/admin")
+ .headers(headers -> headers.setBasicAuth("user", "password"))
+ .exchange()
+ .expectStatus().isForbidden();
+ }
+
@Test
public void customAccessDeniedHandler() {
SecurityWebFilterChain securityWebFilter = this.http
@@ -129,6 +203,33 @@ public class ExceptionHandlingSpecTests {
.expectStatus().isBadRequest();
}
+ @Test
+ public void requestWhenCustomAccessDeniedHandlerInLambdaThenCustomAccessDeniedHandlerUsed()
+ throws Exception {
+ SecurityWebFilterChain securityWebFilter = this.http
+ .httpBasic(withDefaults())
+ .authorizeExchange(exchanges ->
+ exchanges
+ .anyExchange().hasRole("ADMIN")
+ )
+ .exceptionHandling(exceptionHandling ->
+ exceptionHandling
+ .accessDeniedHandler(httpStatusServerAccessDeniedHandler(HttpStatus.BAD_REQUEST))
+ )
+ .build();
+
+ WebTestClient client = WebTestClientBuilder
+ .bindToWebFilters(securityWebFilter)
+ .build();
+
+ client
+ .get()
+ .uri("/admin")
+ .headers(headers -> headers.setBasicAuth("user", "password"))
+ .exchange()
+ .expectStatus().isBadRequest();
+ }
+
private ServerAuthenticationEntryPoint redirectServerAuthenticationEntryPoint(String location) {
return new RedirectServerAuthenticationEntryPoint(location);
}
diff --git a/config/src/test/java/org/springframework/security/config/web/server/FormLoginTests.java b/config/src/test/java/org/springframework/security/config/web/server/FormLoginTests.java
index 96a174f3b6..ac85c3d691 100644
--- a/config/src/test/java/org/springframework/security/config/web/server/FormLoginTests.java
+++ b/config/src/test/java/org/springframework/security/config/web/server/FormLoginTests.java
@@ -46,6 +46,7 @@ import static org.mockito.ArgumentMatchers.any;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.springframework.security.config.Customizer.withDefaults;
/**
* @author Rob Winch
@@ -96,6 +97,49 @@ public class FormLoginTests {
.assertLogout();
}
+ @Test
+ public void formLoginWhenDefaultsInLambdaThenCreatesDefaultLoginPage() throws Exception {
+ SecurityWebFilterChain securityWebFilter = this.http
+ .authorizeExchange(exchanges ->
+ exchanges
+ .anyExchange().authenticated()
+ )
+ .formLogin(withDefaults())
+ .build();
+
+ WebTestClient webTestClient = WebTestClientBuilder
+ .bindToWebFilters(securityWebFilter)
+ .build();
+
+ WebDriver driver = WebTestClientHtmlUnitDriverBuilder
+ .webTestClientSetup(webTestClient)
+ .build();
+
+ DefaultLoginPage loginPage = HomePage.to(driver, DefaultLoginPage.class)
+ .assertAt();
+
+ loginPage = loginPage.loginForm()
+ .username("user")
+ .password("invalid")
+ .submit(DefaultLoginPage.class)
+ .assertError();
+
+ HomePage homePage = loginPage.loginForm()
+ .username("user")
+ .password("password")
+ .submit(HomePage.class);
+
+ homePage.assertAt();
+
+ loginPage = DefaultLogoutPage.to(driver)
+ .assertAt()
+ .logout();
+
+ loginPage
+ .assertAt()
+ .assertLogout();
+ }
+
@Test
public void customLoginPage() {
SecurityWebFilterChain securityWebFilter = this.http
@@ -128,6 +172,40 @@ public class FormLoginTests {
homePage.assertAt();
}
+ @Test
+ public void formLoginWhenCustomLoginPageInLambdaThenUsed() throws Exception {
+ SecurityWebFilterChain securityWebFilter = this.http
+ .authorizeExchange(exchanges ->
+ exchanges
+ .pathMatchers("/login").permitAll()
+ .anyExchange().authenticated()
+ )
+ .formLogin(formLogin ->
+ formLogin
+ .loginPage("/login")
+ )
+ .build();
+
+ WebTestClient webTestClient = WebTestClient
+ .bindToController(new CustomLoginPageController(), new WebTestClientBuilder.Http200RestController())
+ .webFilter(new WebFilterChainProxy(securityWebFilter))
+ .build();
+
+ WebDriver driver = WebTestClientHtmlUnitDriverBuilder
+ .webTestClientSetup(webTestClient)
+ .build();
+
+ CustomLoginPage loginPage = HomePage.to(driver, CustomLoginPage.class)
+ .assertAt();
+
+ HomePage homePage = loginPage.loginForm()
+ .username("user")
+ .password("password")
+ .submit(HomePage.class);
+
+ homePage.assertAt();
+ }
+
@Test
public void authenticationSuccess() {
SecurityWebFilterChain securityWebFilter = this.http
diff --git a/config/src/test/java/org/springframework/security/config/web/server/HeaderSpecTests.java b/config/src/test/java/org/springframework/security/config/web/server/HeaderSpecTests.java
index 98027922f1..22cd608217 100644
--- a/config/src/test/java/org/springframework/security/config/web/server/HeaderSpecTests.java
+++ b/config/src/test/java/org/springframework/security/config/web/server/HeaderSpecTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2018 the original author or authors.
+ * Copyright 2002-2019 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.
@@ -39,6 +39,7 @@ import org.springframework.test.web.reactive.server.FluxExchangeResult;
import org.springframework.test.web.reactive.server.WebTestClient;
import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat;
+import static org.springframework.security.config.Customizer.withDefaults;
/**
* Tests for {@link ServerHttpSecurity.HeaderSpec}.
@@ -49,7 +50,7 @@ import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat;
*/
public class HeaderSpecTests {
- private ServerHttpSecurity.HeaderSpec headers = ServerHttpSecurity.http().headers();
+ private ServerHttpSecurity http = ServerHttpSecurity.http();
private HttpHeaders expectedHeaders = new HttpHeaders();
@@ -72,14 +73,23 @@ public class HeaderSpecTests {
public void headersWhenDisableThenNoSecurityHeaders() {
new HashSet<>(this.expectedHeaders.keySet()).forEach(this::expectHeaderNamesNotPresent);
- this.headers.disable();
+ this.http.headers().disable();
+
+ assertHeaders();
+ }
+
+ @Test
+ public void headersWhenDisableInLambdaThenNoSecurityHeaders() throws Exception {
+ new HashSet<>(this.expectedHeaders.keySet()).forEach(this::expectHeaderNamesNotPresent);
+
+ this.http.headers(headers -> headers.disable());
assertHeaders();
}
@Test
public void headersWhenDisableAndInvokedExplicitlyThenDefautsUsed() {
- this.headers.disable()
+ this.http.headers().disable()
.headers();
assertHeaders();
@@ -87,13 +97,34 @@ public class HeaderSpecTests {
@Test
public void headersWhenDefaultsThenAllDefaultsWritten() {
+ this.http.headers();
+
+ assertHeaders();
+ }
+
+ @Test
+ public void headersWhenDefaultsInLambdaThenAllDefaultsWritten() throws Exception {
+ this.http.headers(withDefaults());
+
assertHeaders();
}
@Test
public void headersWhenCacheDisableThenCacheNotWritten() {
expectHeaderNamesNotPresent(HttpHeaders.CACHE_CONTROL, HttpHeaders.PRAGMA, HttpHeaders.EXPIRES);
- this.headers.cache().disable();
+ this.http.headers().cache().disable();
+
+ assertHeaders();
+ }
+
+
+ @Test
+ public void headersWhenCacheDisableInLambdaThenCacheNotWritten() throws Exception {
+ expectHeaderNamesNotPresent(HttpHeaders.CACHE_CONTROL, HttpHeaders.PRAGMA, HttpHeaders.EXPIRES);
+ this.http
+ .headers(headers ->
+ headers.cache(cache -> cache.disable())
+ );
assertHeaders();
}
@@ -101,7 +132,18 @@ public class HeaderSpecTests {
@Test
public void headersWhenContentOptionsDisableThenContentTypeOptionsNotWritten() {
expectHeaderNamesNotPresent(ContentTypeOptionsServerHttpHeadersWriter.X_CONTENT_OPTIONS);
- this.headers.contentTypeOptions().disable();
+ this.http.headers().contentTypeOptions().disable();
+
+ assertHeaders();
+ }
+
+ @Test
+ public void headersWhenContentOptionsDisableInLambdaThenContentTypeOptionsNotWritten() throws Exception {
+ expectHeaderNamesNotPresent(ContentTypeOptionsServerHttpHeadersWriter.X_CONTENT_OPTIONS);
+ this.http
+ .headers(headers ->
+ headers.contentTypeOptions(contentTypeOptions -> contentTypeOptions.disable())
+ );
assertHeaders();
}
@@ -109,7 +151,18 @@ public class HeaderSpecTests {
@Test
public void headersWhenHstsDisableThenHstsNotWritten() {
expectHeaderNamesNotPresent(StrictTransportSecurityServerHttpHeadersWriter.STRICT_TRANSPORT_SECURITY);
- this.headers.hsts().disable();
+ this.http.headers().hsts().disable();
+
+ assertHeaders();
+ }
+
+ @Test
+ public void headersWhenHstsDisableInLambdaThenHstsNotWritten() throws Exception {
+ expectHeaderNamesNotPresent(StrictTransportSecurityServerHttpHeadersWriter.STRICT_TRANSPORT_SECURITY);
+ this.http
+ .headers(headers ->
+ headers.hsts(hsts -> hsts.disable())
+ );
assertHeaders();
}
@@ -118,28 +171,73 @@ public class HeaderSpecTests {
public void headersWhenHstsCustomThenCustomHstsWritten() {
this.expectedHeaders.remove(StrictTransportSecurityServerHttpHeadersWriter.STRICT_TRANSPORT_SECURITY);
this.expectedHeaders.add(StrictTransportSecurityServerHttpHeadersWriter.STRICT_TRANSPORT_SECURITY, "max-age=60");
- this.headers.hsts()
+ this.http.headers().hsts()
.maxAge(Duration.ofSeconds(60))
.includeSubdomains(false);
assertHeaders();
}
+ @Test
+ public void headersWhenHstsCustomInLambdaThenCustomHstsWritten() throws Exception {
+ this.expectedHeaders.remove(StrictTransportSecurityServerHttpHeadersWriter.STRICT_TRANSPORT_SECURITY);
+ this.expectedHeaders.add(StrictTransportSecurityServerHttpHeadersWriter.STRICT_TRANSPORT_SECURITY, "max-age=60");
+ this.http
+ .headers(headers ->
+ headers
+ .hsts(hsts ->
+ hsts
+ .maxAge(Duration.ofSeconds(60))
+ .includeSubdomains(false)
+ )
+ );
+
+ assertHeaders();
+ }
+
@Test
public void headersWhenHstsCustomWithPreloadThenCustomHstsWritten() {
this.expectedHeaders.remove(StrictTransportSecurityServerHttpHeadersWriter.STRICT_TRANSPORT_SECURITY);
this.expectedHeaders.add(StrictTransportSecurityServerHttpHeadersWriter.STRICT_TRANSPORT_SECURITY, "max-age=60 ; includeSubDomains ; preload");
- this.headers.hsts()
+ this.http.headers().hsts()
.maxAge(Duration.ofSeconds(60))
.preload(true);
assertHeaders();
}
+ @Test
+ public void headersWhenHstsCustomWithPreloadInLambdaThenCustomHstsWritten() throws Exception {
+ this.expectedHeaders.remove(StrictTransportSecurityServerHttpHeadersWriter.STRICT_TRANSPORT_SECURITY);
+ this.expectedHeaders.add(StrictTransportSecurityServerHttpHeadersWriter.STRICT_TRANSPORT_SECURITY, "max-age=60 ; includeSubDomains ; preload");
+ this.http
+ .headers(headers ->
+ headers
+ .hsts(hsts ->
+ hsts
+ .maxAge(Duration.ofSeconds(60))
+ .preload(true)
+ )
+ );
+
+ assertHeaders();
+ }
+
@Test
public void headersWhenFrameOptionsDisableThenFrameOptionsNotWritten() {
expectHeaderNamesNotPresent(XFrameOptionsServerHttpHeadersWriter.X_FRAME_OPTIONS);
- this.headers.frameOptions().disable();
+ this.http.headers().frameOptions().disable();
+
+ assertHeaders();
+ }
+
+ @Test
+ public void headersWhenFrameOptionsDisableInLambdaThenFrameOptionsNotWritten() throws Exception {
+ expectHeaderNamesNotPresent(XFrameOptionsServerHttpHeadersWriter.X_FRAME_OPTIONS);
+ this.http
+ .headers(headers ->
+ headers.frameOptions(frameOptions -> frameOptions.disable())
+ );
assertHeaders();
}
@@ -147,17 +245,43 @@ public class HeaderSpecTests {
@Test
public void headersWhenFrameOptionsModeThenFrameOptionsCustomMode() {
this.expectedHeaders.set(XFrameOptionsServerHttpHeadersWriter.X_FRAME_OPTIONS, "SAMEORIGIN");
- this.headers
+ this.http.headers()
.frameOptions()
.mode(XFrameOptionsServerHttpHeadersWriter.Mode.SAMEORIGIN);
assertHeaders();
}
+ @Test
+ public void headersWhenFrameOptionsModeInLambdaThenFrameOptionsCustomMode() throws Exception {
+ this.expectedHeaders.set(XFrameOptionsServerHttpHeadersWriter.X_FRAME_OPTIONS, "SAMEORIGIN");
+ this.http
+ .headers(headers ->
+ headers
+ .frameOptions(frameOptions ->
+ frameOptions
+ .mode(XFrameOptionsServerHttpHeadersWriter.Mode.SAMEORIGIN)
+ )
+ );
+
+ assertHeaders();
+ }
+
@Test
public void headersWhenXssProtectionDisableThenXssProtectionNotWritten() {
expectHeaderNamesNotPresent("X-Xss-Protection");
- this.headers.xssProtection().disable();
+ this.http.headers().xssProtection().disable();
+
+ assertHeaders();
+ }
+
+ @Test
+ public void headersWhenXssProtectionDisableInLambdaThenXssProtectionNotWritten() throws Exception {
+ expectHeaderNamesNotPresent("X-Xss-Protection");
+ this.http
+ .headers(headers ->
+ headers.xssProtection(xssProtection -> xssProtection.disable())
+ );
assertHeaders();
}
@@ -168,7 +292,7 @@ public class HeaderSpecTests {
this.expectedHeaders.add(FeaturePolicyServerHttpHeadersWriter.FEATURE_POLICY,
policyDirectives);
- this.headers.featurePolicy(policyDirectives);
+ this.http.headers().featurePolicy(policyDirectives);
assertHeaders();
}
@@ -179,7 +303,39 @@ public class HeaderSpecTests {
this.expectedHeaders.add(ContentSecurityPolicyServerHttpHeadersWriter.CONTENT_SECURITY_POLICY,
policyDirectives);
- this.headers.contentSecurityPolicy(policyDirectives);
+ this.http.headers().contentSecurityPolicy(policyDirectives);
+
+ assertHeaders();
+ }
+
+ @Test
+ public void headersWhenContentSecurityPolicyEnabledWithDefaultsInLambdaThenDefaultPolicyWritten() throws Exception {
+ String expectedPolicyDirectives = "default-src 'self'";
+ this.expectedHeaders.add(ContentSecurityPolicyServerHttpHeadersWriter.CONTENT_SECURITY_POLICY,
+ expectedPolicyDirectives);
+
+ this.http
+ .headers(headers ->
+ headers.contentSecurityPolicy(withDefaults())
+ );
+
+ assertHeaders();
+ }
+
+ @Test
+ public void headersWhenContentSecurityPolicyEnabledInLambdaThenContentSecurityPolicyWritten() throws Exception {
+ String policyDirectives = "default-src 'self' *.trusted.com";
+ this.expectedHeaders.add(ContentSecurityPolicyServerHttpHeadersWriter.CONTENT_SECURITY_POLICY,
+ policyDirectives);
+
+ this.http
+ .headers(headers ->
+ headers
+ .contentSecurityPolicy(contentSecurityPolicy ->
+ contentSecurityPolicy
+ .policyDirectives(policyDirectives)
+ )
+ );
assertHeaders();
}
@@ -188,7 +344,20 @@ public class HeaderSpecTests {
public void headersWhenReferrerPolicyEnabledThenFeaturePolicyWritten() {
this.expectedHeaders.add(ReferrerPolicyServerHttpHeadersWriter.REFERRER_POLICY,
ReferrerPolicy.NO_REFERRER.getPolicy());
- this.headers.referrerPolicy();
+ this.http.headers().referrerPolicy();
+
+ assertHeaders();
+ }
+
+ @Test
+ public void headersWhenReferrerPolicyEnabledInLambdaThenReferrerPolicyWritten() throws Exception {
+ this.expectedHeaders.add(ReferrerPolicyServerHttpHeadersWriter.REFERRER_POLICY,
+ ReferrerPolicy.NO_REFERRER.getPolicy());
+ this.http
+ .headers(headers ->
+ headers
+ .referrerPolicy(withDefaults())
+ );
assertHeaders();
}
@@ -197,7 +366,23 @@ public class HeaderSpecTests {
public void headersWhenReferrerPolicyCustomEnabledThenFeaturePolicyCustomWritten() {
this.expectedHeaders.add(ReferrerPolicyServerHttpHeadersWriter.REFERRER_POLICY,
ReferrerPolicy.NO_REFERRER_WHEN_DOWNGRADE.getPolicy());
- this.headers.referrerPolicy(ReferrerPolicy.NO_REFERRER_WHEN_DOWNGRADE);
+ this.http.headers().referrerPolicy(ReferrerPolicy.NO_REFERRER_WHEN_DOWNGRADE);
+
+ assertHeaders();
+ }
+
+ @Test
+ public void headersWhenReferrerPolicyCustomEnabledInLambdaThenCustomReferrerPolicyWritten() throws Exception {
+ this.expectedHeaders.add(ReferrerPolicyServerHttpHeadersWriter.REFERRER_POLICY,
+ ReferrerPolicy.NO_REFERRER_WHEN_DOWNGRADE.getPolicy());
+ this.http
+ .headers(headers ->
+ headers
+ .referrerPolicy(referrerPolicy ->
+ referrerPolicy
+ .policy(ReferrerPolicy.NO_REFERRER_WHEN_DOWNGRADE)
+ )
+ );
assertHeaders();
}
@@ -228,6 +413,6 @@ public class HeaderSpecTests {
}
private WebTestClient buildClient() {
- return WebTestClientBuilder.bindToWebFilters(this.headers.and().build()).build();
+ return WebTestClientBuilder.bindToWebFilters(this.http.build()).build();
}
}
diff --git a/config/src/test/java/org/springframework/security/config/web/server/HttpsRedirectSpecTests.java b/config/src/test/java/org/springframework/security/config/web/server/HttpsRedirectSpecTests.java
index 1413bd61e3..5e2597e2f8 100644
--- a/config/src/test/java/org/springframework/security/config/web/server/HttpsRedirectSpecTests.java
+++ b/config/src/test/java/org/springframework/security/config/web/server/HttpsRedirectSpecTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2018 the original author or authors.
+ * Copyright 2002-2019 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.
@@ -33,6 +33,7 @@ import org.springframework.web.reactive.config.EnableWebFlux;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
+import static org.springframework.security.config.Customizer.withDefaults;
/**
* Tests for {@link HttpsRedirectSpecTests}
@@ -71,6 +72,17 @@ public class HttpsRedirectSpecTests {
.expectHeader().valueEquals(HttpHeaders.LOCATION, "https://localhost");
}
+ @Test
+ public void getWhenInsecureAndRedirectConfiguredInLambdaThenRespondsWithRedirectToSecure() {
+ this.spring.register(RedirectToHttpsInLambdaConfig.class).autowire();
+
+ this.client.get()
+ .uri("http://localhost")
+ .exchange()
+ .expectStatus().isFound()
+ .expectHeader().valueEquals(HttpHeaders.LOCATION, "https://localhost");
+ }
+
@Test
public void getWhenInsecureAndPathRequiresTransportSecurityThenRedirects() {
this.spring.register(SometimesRedirectToHttpsConfig.class).autowire();
@@ -87,6 +99,22 @@ public class HttpsRedirectSpecTests {
.expectHeader().valueEquals(HttpHeaders.LOCATION, "https://localhost:8443/secure");
}
+ @Test
+ public void getWhenInsecureAndPathRequiresTransportSecurityInLambdaThenRedirects() {
+ this.spring.register(SometimesRedirectToHttpsInLambdaConfig.class).autowire();
+
+ this.client.get()
+ .uri("http://localhost:8080")
+ .exchange()
+ .expectStatus().isNotFound();
+
+ this.client.get()
+ .uri("http://localhost:8080/secure")
+ .exchange()
+ .expectStatus().isFound()
+ .expectHeader().valueEquals(HttpHeaders.LOCATION, "https://localhost:8443/secure");
+ }
+
@Test
public void getWhenInsecureAndUsingCustomPortMapperThenRespondsWithRedirectToSecurePort() {
this.spring.register(RedirectToHttpsViaCustomPortsConfig.class).autowire();
@@ -101,6 +129,20 @@ public class HttpsRedirectSpecTests {
.expectHeader().valueEquals(HttpHeaders.LOCATION, "https://localhost:4443");
}
+ @Test
+ public void getWhenInsecureAndUsingCustomPortMapperInLambdaThenRespondsWithRedirectToSecurePort() {
+ this.spring.register(RedirectToHttpsViaCustomPortsInLambdaConfig.class).autowire();
+
+ PortMapper portMapper = this.spring.getContext().getBean(PortMapper.class);
+ when(portMapper.lookupHttpsPort(4080)).thenReturn(4443);
+
+ this.client.get()
+ .uri("http://localhost:4080")
+ .exchange()
+ .expectStatus().isFound()
+ .expectHeader().valueEquals(HttpHeaders.LOCATION, "https://localhost:4443");
+ }
+
@EnableWebFlux
@EnableWebFluxSecurity
static class RedirectToHttpConfig {
@@ -115,6 +157,21 @@ public class HttpsRedirectSpecTests {
}
}
+
+ @EnableWebFlux
+ @EnableWebFluxSecurity
+ static class RedirectToHttpsInLambdaConfig {
+ @Bean
+ SecurityWebFilterChain springSecurity(ServerHttpSecurity http) throws Exception {
+ // @formatter:off
+ http
+ .redirectToHttps(withDefaults());
+ // @formatter:on
+
+ return http.build();
+ }
+ }
+
@EnableWebFlux
@EnableWebFluxSecurity
static class SometimesRedirectToHttpsConfig {
@@ -130,6 +187,24 @@ public class HttpsRedirectSpecTests {
}
}
+
+ @EnableWebFlux
+ @EnableWebFluxSecurity
+ static class SometimesRedirectToHttpsInLambdaConfig {
+ @Bean
+ SecurityWebFilterChain springSecurity(ServerHttpSecurity http) throws Exception {
+ // @formatter:off
+ http
+ .redirectToHttps(redirectToHttps ->
+ redirectToHttps
+ .httpsRedirectWhen(new PathPatternParserServerWebExchangeMatcher("/secure"))
+ );
+ // @formatter:on
+
+ return http.build();
+ }
+ }
+
@EnableWebFlux
@EnableWebFluxSecurity
static class RedirectToHttpsViaCustomPortsConfig {
@@ -149,4 +224,26 @@ public class HttpsRedirectSpecTests {
return mock(PortMapper.class);
}
}
+
+ @EnableWebFlux
+ @EnableWebFluxSecurity
+ static class RedirectToHttpsViaCustomPortsInLambdaConfig {
+ @Bean
+ SecurityWebFilterChain springSecurity(ServerHttpSecurity http) throws Exception {
+ // @formatter:off
+ http
+ .redirectToHttps(redirectToHttps ->
+ redirectToHttps
+ .portMapper(portMapper())
+ );
+ // @formatter:on
+
+ return http.build();
+ }
+
+ @Bean
+ public PortMapper portMapper() {
+ return mock(PortMapper.class);
+ }
+ }
}
diff --git a/config/src/test/java/org/springframework/security/config/web/server/LogoutSpecTests.java b/config/src/test/java/org/springframework/security/config/web/server/LogoutSpecTests.java
index a8dbc2ac26..9a01fba23f 100644
--- a/config/src/test/java/org/springframework/security/config/web/server/LogoutSpecTests.java
+++ b/config/src/test/java/org/springframework/security/config/web/server/LogoutSpecTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2017 the original author or authors.
+ * Copyright 2002-2019 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.
@@ -25,6 +25,8 @@ import org.springframework.security.web.server.util.matcher.ServerWebExchangeMat
import org.springframework.test.web.reactive.server.WebTestClient;
import org.springframework.security.test.web.reactive.server.WebTestClientBuilder;
+import static org.springframework.security.config.Customizer.withDefaults;
+
/**
* @author Shazin Sadakath
* @since 5.0
@@ -117,4 +119,49 @@ public class LogoutSpecTests {
.assertAt()
.assertLogout();
}
+
+ @Test
+ public void logoutWhenCustomLogoutInLambdaThenCustomLogoutUsed() throws Exception {
+ SecurityWebFilterChain securityWebFilter = this.http
+ .authorizeExchange(authorizeExchange ->
+ authorizeExchange
+ .anyExchange().authenticated()
+ )
+ .formLogin(withDefaults())
+ .logout(logout ->
+ logout
+ .requiresLogout(ServerWebExchangeMatchers.pathMatchers("/custom-logout"))
+ )
+ .build();
+
+ WebTestClient webTestClient = WebTestClientBuilder
+ .bindToWebFilters(securityWebFilter)
+ .build();
+
+ WebDriver driver = WebTestClientHtmlUnitDriverBuilder
+ .webTestClientSetup(webTestClient)
+ .build();
+
+ FormLoginTests.DefaultLoginPage loginPage = FormLoginTests.HomePage.to(driver, FormLoginTests.DefaultLoginPage.class)
+ .assertAt();
+
+ loginPage = loginPage.loginForm()
+ .username("user")
+ .password("invalid")
+ .submit(FormLoginTests.DefaultLoginPage.class)
+ .assertError();
+
+ FormLoginTests.HomePage homePage = loginPage.loginForm()
+ .username("user")
+ .password("password")
+ .submit(FormLoginTests.HomePage.class);
+
+ homePage.assertAt();
+
+ driver.get("http://localhost/custom-logout");
+
+ FormLoginTests.DefaultLoginPage.create(driver)
+ .assertAt()
+ .assertLogout();
+ }
}
diff --git a/config/src/test/java/org/springframework/security/config/web/server/OAuth2ClientSpecTests.java b/config/src/test/java/org/springframework/security/config/web/server/OAuth2ClientSpecTests.java
index 9982ca608a..4fa2585095 100644
--- a/config/src/test/java/org/springframework/security/config/web/server/OAuth2ClientSpecTests.java
+++ b/config/src/test/java/org/springframework/security/config/web/server/OAuth2ClientSpecTests.java
@@ -185,4 +185,48 @@ public class OAuth2ClientSpecTests {
return http.build();
}
}
+
+ @Test
+ public void oauth2ClientWhenCustomObjectsInLambdaThenUsed() {
+ this.spring.register(ClientRegistrationConfig.class, OAuth2ClientInLambdaCustomConfig.class, AuthorizedClientController.class).autowire();
+
+ OAuth2ClientInLambdaCustomConfig config = this.spring.getContext().getBean(OAuth2ClientInLambdaCustomConfig.class);
+
+ ServerAuthenticationConverter converter = config.authenticationConverter;
+ ReactiveAuthenticationManager manager = config.manager;
+
+ OAuth2AuthorizationExchange exchange = TestOAuth2AuthorizationExchanges.success();
+ OAuth2AccessToken accessToken = TestOAuth2AccessTokens.noScopes();
+
+ OAuth2AuthorizationCodeAuthenticationToken result = new OAuth2AuthorizationCodeAuthenticationToken(this.registration, exchange, accessToken);
+
+ when(converter.convert(any())).thenReturn(Mono.just(new TestingAuthenticationToken("a", "b", "c")));
+ when(manager.authenticate(any())).thenReturn(Mono.just(result));
+
+ this.client.get()
+ .uri("/authorize/oauth2/code/registration-id")
+ .exchange()
+ .expectStatus().is3xxRedirection();
+
+ verify(converter).convert(any());
+ verify(manager).authenticate(any());
+ }
+
+ @Configuration
+ static class OAuth2ClientInLambdaCustomConfig {
+ ReactiveAuthenticationManager manager = mock(ReactiveAuthenticationManager.class);
+
+ ServerAuthenticationConverter authenticationConverter = mock(ServerAuthenticationConverter.class);
+
+ @Bean
+ public SecurityWebFilterChain springSecurityFilter(ServerHttpSecurity http) throws Exception {
+ http
+ .oauth2Client(oauth2Client ->
+ oauth2Client
+ .authenticationConverter(this.authenticationConverter)
+ .authenticationManager(this.manager)
+ );
+ return http.build();
+ }
+ }
}
diff --git a/config/src/test/java/org/springframework/security/config/web/server/OAuth2LoginTests.java b/config/src/test/java/org/springframework/security/config/web/server/OAuth2LoginTests.java
index 44bb47bd3e..c7cbec05a8 100644
--- a/config/src/test/java/org/springframework/security/config/web/server/OAuth2LoginTests.java
+++ b/config/src/test/java/org/springframework/security/config/web/server/OAuth2LoginTests.java
@@ -261,6 +261,87 @@ public class OAuth2LoginTests {
}
}
+ @Test
+ public void oauth2LoginWhenCustomObjectsInLambdaThenUsed() {
+ this.spring.register(OAuth2LoginWithSingleClientRegistrations.class,
+ OAuth2LoginMockAuthenticationManagerInLambdaConfig.class).autowire();
+
+ String redirectLocation = "/custom-redirect-location";
+
+ WebTestClient webTestClient = WebTestClientBuilder
+ .bindToWebFilters(this.springSecurity)
+ .build();
+
+ OAuth2LoginMockAuthenticationManagerInLambdaConfig config = this.spring.getContext()
+ .getBean(OAuth2LoginMockAuthenticationManagerInLambdaConfig.class);
+ ServerAuthenticationConverter converter = config.authenticationConverter;
+ ReactiveAuthenticationManager manager = config.manager;
+ ServerWebExchangeMatcher matcher = config.matcher;
+ ServerOAuth2AuthorizationRequestResolver resolver = config.resolver;
+ ServerAuthenticationSuccessHandler successHandler = config.successHandler;
+
+ OAuth2AuthorizationExchange exchange = TestOAuth2AuthorizationExchanges.success();
+ OAuth2User user = TestOAuth2Users.create();
+ OAuth2AccessToken accessToken = TestOAuth2AccessTokens.noScopes();
+
+ OAuth2LoginAuthenticationToken result = new OAuth2LoginAuthenticationToken(github, exchange, user, user.getAuthorities(), accessToken);
+
+ when(converter.convert(any())).thenReturn(Mono.just(new TestingAuthenticationToken("a", "b", "c")));
+ when(manager.authenticate(any())).thenReturn(Mono.just(result));
+ when(matcher.matches(any())).thenReturn(ServerWebExchangeMatcher.MatchResult.match());
+ when(resolver.resolve(any())).thenReturn(Mono.empty());
+ when(successHandler.onAuthenticationSuccess(any(), any())).thenAnswer((Answer>) invocation -> {
+ WebFilterExchange webFilterExchange = invocation.getArgument(0);
+ Authentication authentication = invocation.getArgument(1);
+
+ return new RedirectServerAuthenticationSuccessHandler(redirectLocation)
+ .onAuthenticationSuccess(webFilterExchange, authentication);
+ });
+
+ webTestClient.get()
+ .uri("/login/oauth2/code/github")
+ .exchange()
+ .expectStatus().is3xxRedirection()
+ .expectHeader().valueEquals("Location", redirectLocation);
+
+ verify(converter).convert(any());
+ verify(manager).authenticate(any());
+ verify(matcher).matches(any());
+ verify(resolver).resolve(any());
+ verify(successHandler).onAuthenticationSuccess(any(), any());
+ }
+
+ @Configuration
+ static class OAuth2LoginMockAuthenticationManagerInLambdaConfig {
+ ReactiveAuthenticationManager manager = mock(ReactiveAuthenticationManager.class);
+
+ ServerAuthenticationConverter authenticationConverter = mock(ServerAuthenticationConverter.class);
+
+ ServerWebExchangeMatcher matcher = mock(ServerWebExchangeMatcher.class);
+
+ ServerOAuth2AuthorizationRequestResolver resolver = mock(ServerOAuth2AuthorizationRequestResolver.class);
+
+ ServerAuthenticationSuccessHandler successHandler = mock(ServerAuthenticationSuccessHandler.class);
+
+ @Bean
+ public SecurityWebFilterChain springSecurityFilter(ServerHttpSecurity http) throws Exception {
+ http
+ .authorizeExchange(exchanges ->
+ exchanges
+ .anyExchange().authenticated()
+ )
+ .oauth2Login(oauth2Login ->
+ oauth2Login
+ .authenticationConverter(authenticationConverter)
+ .authenticationManager(manager)
+ .authenticationMatcher(matcher)
+ .authorizationRequestResolver(resolver)
+ .authenticationSuccessHandler(successHandler)
+ );
+ return http.build();
+ }
+ }
+
@Test
public void oauth2LoginWhenCustomBeansThenUsed() {
this.spring.register(OAuth2LoginWithMultipleClientRegistrations.class,
diff --git a/config/src/test/java/org/springframework/security/config/web/server/OAuth2ResourceServerSpecTests.java b/config/src/test/java/org/springframework/security/config/web/server/OAuth2ResourceServerSpecTests.java
index a9427cb36a..fa8b937d6c 100644
--- a/config/src/test/java/org/springframework/security/config/web/server/OAuth2ResourceServerSpecTests.java
+++ b/config/src/test/java/org/springframework/security/config/web/server/OAuth2ResourceServerSpecTests.java
@@ -175,6 +175,27 @@ public class OAuth2ResourceServerSpecTests {
.expectHeader().value(HttpHeaders.WWW_AUTHENTICATE, startsWith("Bearer error=\"invalid_token\""));
}
+ @Test
+ public void getWhenValidTokenAndPublicKeyInLambdaThenReturnsOk() {
+ this.spring.register(PublicKeyInLambdaConfig.class, RootController.class).autowire();
+
+ this.client.get()
+ .headers(headers -> headers.setBearerAuth(this.messageReadToken))
+ .exchange()
+ .expectStatus().isOk();
+ }
+
+ @Test
+ public void getWhenExpiredTokenAndPublicKeyInLambdaThenReturnsInvalidToken() {
+ this.spring.register(PublicKeyInLambdaConfig.class).autowire();
+
+ this.client.get()
+ .headers(headers -> headers.setBearerAuth(this.expired))
+ .exchange()
+ .expectStatus().isUnauthorized()
+ .expectHeader().value(HttpHeaders.WWW_AUTHENTICATE, startsWith("Bearer error=\"invalid_token\""));
+ }
+
@Test
public void getWhenValidUsingPlaceholderThenReturnsOk() {
this.spring.register(PlaceholderConfig.class, RootController.class).autowire();
@@ -213,6 +234,18 @@ public class OAuth2ResourceServerSpecTests {
.expectStatus().isOk();
}
+ @Test
+ public void getWhenUsingJwkSetUriInLambdaThenConsultsAccordingly() {
+ this.spring.register(JwkSetUriInLambdaConfig.class, RootController.class).autowire();
+
+ MockWebServer mockWebServer = this.spring.getContext().getBean(MockWebServer.class);
+ mockWebServer.enqueue(new MockResponse().setBody(this.jwkSet));
+
+ this.client.get()
+ .headers(headers -> headers.setBearerAuth(this.messageReadTokenWithKid))
+ .exchange()
+ .expectStatus().isOk();
+ }
@Test
public void getWhenUsingCustomAuthenticationManagerThenUsesItAccordingly() {
@@ -230,6 +263,22 @@ public class OAuth2ResourceServerSpecTests {
.expectHeader().value(HttpHeaders.WWW_AUTHENTICATE, startsWith("Bearer error=\"mock-failure\""));
}
+ @Test
+ public void getWhenUsingCustomAuthenticationManagerInLambdaThenUsesItAccordingly() {
+ this.spring.register(CustomAuthenticationManagerInLambdaConfig.class).autowire();
+
+ ReactiveAuthenticationManager authenticationManager = this.spring.getContext().getBean(
+ ReactiveAuthenticationManager.class);
+ when(authenticationManager.authenticate(any(Authentication.class)))
+ .thenReturn(Mono.error(new OAuth2AuthenticationException(new OAuth2Error("mock-failure"))));
+
+ this.client.get()
+ .headers(headers -> headers.setBearerAuth(this.messageReadToken))
+ .exchange()
+ .expectStatus().isUnauthorized()
+ .expectHeader().value(HttpHeaders.WWW_AUTHENTICATE, startsWith("Bearer error=\"mock-failure\""));
+ }
+
@Test
public void getWhenUsingCustomAuthenticationManagerResolverThenUsesItAccordingly() {
this.spring.register(CustomAuthenticationManagerResolverConfig.class).autowire();
@@ -396,6 +445,18 @@ public class OAuth2ResourceServerSpecTests {
.expectStatus().isOk();
}
+ @Test
+ public void introspectWhenValidAndIntrospectionInLambdaThenReturnsOk() {
+ this.spring.register(IntrospectionInLambdaConfig.class, RootController.class).autowire();
+ this.spring.getContext().getBean(MockWebServer.class)
+ .setDispatcher(requiresAuth(clientId, clientSecret, active));
+
+ this.client.get()
+ .headers(headers -> headers.setBearerAuth(this.messageReadToken))
+ .exchange()
+ .expectStatus().isOk();
+ }
+
@EnableWebFlux
@EnableWebFluxSecurity
static class PublicKeyConfig {
@@ -416,6 +477,30 @@ public class OAuth2ResourceServerSpecTests {
}
}
+ @EnableWebFlux
+ @EnableWebFluxSecurity
+ static class PublicKeyInLambdaConfig {
+ @Bean
+ SecurityWebFilterChain springSecurity(ServerHttpSecurity http) throws Exception {
+ // @formatter:off
+ http
+ .authorizeExchange(exchanges ->
+ exchanges
+ .anyExchange().hasAuthority("SCOPE_message:read")
+ )
+ .oauth2ResourceServer(oauth2ResourceServer ->
+ oauth2ResourceServer
+ .jwt(jwt ->
+ jwt
+ .publicKey(publicKey())
+ )
+ );
+ // @formatter:on
+
+ return http.build();
+ }
+ }
+
@EnableWebFlux
@EnableWebFluxSecurity
static class PlaceholderConfig {
@@ -469,6 +554,40 @@ public class OAuth2ResourceServerSpecTests {
}
}
+ @EnableWebFlux
+ @EnableWebFluxSecurity
+ static class JwkSetUriInLambdaConfig {
+ private MockWebServer mockWebServer = new MockWebServer();
+
+ @Bean
+ SecurityWebFilterChain springSecurity(ServerHttpSecurity http) throws Exception {
+ String jwkSetUri = mockWebServer().url("/.well-known/jwks.json").toString();
+
+ // @formatter:off
+ http
+ .oauth2ResourceServer(oauth2ResourceServer ->
+ oauth2ResourceServer
+ .jwt(jwt ->
+ jwt
+ .jwkSetUri(jwkSetUri)
+ )
+ );
+ // @formatter:on
+
+ return http.build();
+ }
+
+ @Bean
+ MockWebServer mockWebServer() {
+ return this.mockWebServer;
+ }
+
+ @PreDestroy
+ void shutdown() throws IOException {
+ this.mockWebServer.shutdown();
+ }
+ }
+
@EnableWebFlux
@EnableWebFluxSecurity
static class CustomDecoderConfig {
@@ -531,6 +650,31 @@ public class OAuth2ResourceServerSpecTests {
}
}
+ @EnableWebFlux
+ @EnableWebFluxSecurity
+ static class CustomAuthenticationManagerInLambdaConfig {
+ @Bean
+ SecurityWebFilterChain springSecurity(ServerHttpSecurity http) throws Exception {
+ // @formatter:off
+ http
+ .oauth2ResourceServer(oauth2ResourceServer ->
+ oauth2ResourceServer
+ .jwt(jwt ->
+ jwt
+ .authenticationManager(authenticationManager())
+ )
+ );
+ // @formatter:on
+
+ return http.build();
+ }
+
+ @Bean
+ ReactiveAuthenticationManager authenticationManager() {
+ return mock(ReactiveAuthenticationManager.class);
+ }
+ }
+
@EnableWebFlux
@EnableWebFluxSecurity
static class CustomAuthenticationManagerResolverConfig {
@@ -670,6 +814,41 @@ public class OAuth2ResourceServerSpecTests {
}
}
+ @EnableWebFlux
+ @EnableWebFluxSecurity
+ static class IntrospectionInLambdaConfig {
+ private MockWebServer mockWebServer = new MockWebServer();
+
+ @Bean
+ SecurityWebFilterChain springSecurity(ServerHttpSecurity http) throws Exception {
+ String introspectionUri = mockWebServer().url("/introspect").toString();
+
+ // @formatter:off
+ http
+ .oauth2ResourceServer(oauth2ResourceServer ->
+ oauth2ResourceServer
+ .opaqueToken(opaqueToken ->
+ opaqueToken
+ .introspectionUri(introspectionUri)
+ .introspectionClientCredentials("client", "secret")
+ )
+ );
+ // @formatter:on
+
+ return http.build();
+ }
+
+ @Bean
+ MockWebServer mockWebServer() {
+ return this.mockWebServer;
+ }
+
+ @PreDestroy
+ void shutdown() throws IOException {
+ this.mockWebServer.shutdown();
+ }
+ }
+
@RestController
static class RootController {
@GetMapping
diff --git a/config/src/test/java/org/springframework/security/config/web/server/RequestCacheTests.java b/config/src/test/java/org/springframework/security/config/web/server/RequestCacheTests.java
index f1b862f28b..3a26d32856 100644
--- a/config/src/test/java/org/springframework/security/config/web/server/RequestCacheTests.java
+++ b/config/src/test/java/org/springframework/security/config/web/server/RequestCacheTests.java
@@ -34,6 +34,7 @@ import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.server.ServerWebExchange;
import static org.assertj.core.api.Assertions.assertThat;
+import static org.springframework.security.config.Customizer.withDefaults;
/**
* @author Rob Winch
@@ -103,6 +104,40 @@ public class RequestCacheTests {
securedPage.assertAt();
}
+ @Test
+ public void requestWhenCustomRequestCacheInLambdaThenCustomCacheUsed() throws Exception {
+ SecurityWebFilterChain securityWebFilter = this.http
+ .authorizeExchange(authorizeExchange ->
+ authorizeExchange
+ .anyExchange().authenticated()
+ )
+ .formLogin(withDefaults())
+ .requestCache(requestCache ->
+ requestCache
+ .requestCache(NoOpServerRequestCache.getInstance())
+ )
+ .build();
+
+ WebTestClient webTestClient = WebTestClient
+ .bindToController(new SecuredPageController(), new WebTestClientBuilder.Http200RestController())
+ .webFilter(new WebFilterChainProxy(securityWebFilter))
+ .build();
+
+ WebDriver driver = WebTestClientHtmlUnitDriverBuilder
+ .webTestClientSetup(webTestClient)
+ .build();
+
+ DefaultLoginPage loginPage = SecuredPage.to(driver, DefaultLoginPage.class)
+ .assertAt();
+
+ HomePage securedPage = loginPage.loginForm()
+ .username("user")
+ .password("password")
+ .submit(HomePage.class);
+
+ securedPage.assertAt();
+ }
+
public static class SecuredPage {
private WebDriver driver;
diff --git a/config/src/test/java/org/springframework/security/config/web/server/ServerHttpSecurityTests.java b/config/src/test/java/org/springframework/security/config/web/server/ServerHttpSecurityTests.java
index daedfb9a6a..37514bffc3 100644
--- a/config/src/test/java/org/springframework/security/config/web/server/ServerHttpSecurityTests.java
+++ b/config/src/test/java/org/springframework/security/config/web/server/ServerHttpSecurityTests.java
@@ -20,8 +20,10 @@ import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.BDDMockito.given;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;
+import static org.springframework.security.config.Customizer.withDefaults;
import java.util.Arrays;
import java.util.List;
@@ -36,6 +38,7 @@ import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
+import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.preauth.x509.X509PrincipalExtractor;
import org.springframework.security.web.server.authentication.ServerX509AuthenticationConverter;
import reactor.core.publisher.Mono;
@@ -236,6 +239,19 @@ public class ServerHttpSecurityTests {
}
+ @Test
+ public void getWhenAnonymousConfiguredThenAuthenticationIsAnonymous() throws Exception {
+ SecurityWebFilterChain securityFilterChain = this.http.anonymous(withDefaults()).build();
+ WebTestClient client = WebTestClientBuilder.bindToControllerAndWebFilters(AnonymousAuthenticationWebFilterTests.HttpMeController.class,
+ securityFilterChain).build();
+
+ client.get()
+ .uri("/me")
+ .exchange()
+ .expectStatus().isOk()
+ .expectBody(String.class).isEqualTo("anonymousUser");
+ }
+
@Test
public void basicWithAnonymous() {
given(this.authenticationManager.authenticate(any())).willReturn(Mono.just(new TestingAuthenticationToken("rob", "rob", "ROLE_USER", "ROLE_ADMIN")));
@@ -283,6 +299,31 @@ public class ServerHttpSecurityTests {
assertThat(result.getResponseCookies().getFirst("SESSION")).isNull();
}
+ @Test
+ public void requestWhenBasicWithRealmNameInLambdaThenRealmNameUsed() throws Exception {
+ this.http.securityContextRepository(new WebSessionServerSecurityContextRepository());
+ HttpBasicServerAuthenticationEntryPoint authenticationEntryPoint = new HttpBasicServerAuthenticationEntryPoint();
+ authenticationEntryPoint.setRealm("myrealm");
+ this.http.httpBasic(httpBasic ->
+ httpBasic.authenticationEntryPoint(authenticationEntryPoint)
+ );
+ this.http.authenticationManager(this.authenticationManager);
+ ServerHttpSecurity.AuthorizeExchangeSpec authorize = this.http.authorizeExchange();
+ authorize.anyExchange().authenticated();
+
+ WebTestClient client = buildClient();
+
+ EntityExchangeResult result = client.get()
+ .uri("/")
+ .exchange()
+ .expectStatus().isUnauthorized()
+ .expectHeader().value(HttpHeaders.WWW_AUTHENTICATE, value -> assertThat(value).contains("myrealm"))
+ .expectBody(String.class)
+ .returnResult();
+
+ assertThat(result.getResponseCookies().getFirst("SESSION")).isNull();
+ }
+
@Test
public void basicWithCustomAuthenticationManager() {
ReactiveAuthenticationManager customAuthenticationManager = mock(ReactiveAuthenticationManager.class);
@@ -302,6 +343,31 @@ public class ServerHttpSecurityTests {
verifyZeroInteractions(this.authenticationManager);
}
+ @Test
+ public void requestWhenBasicWithAuthenticationManagerInLambdaThenAuthenticationManagerUsed() throws Exception {
+ ReactiveAuthenticationManager customAuthenticationManager = mock(ReactiveAuthenticationManager.class);
+ given(customAuthenticationManager.authenticate(any()))
+ .willReturn(Mono.just(new TestingAuthenticationToken("rob", "rob", "ROLE_USER", "ROLE_ADMIN")));
+
+ SecurityWebFilterChain securityFilterChain = this.http
+ .httpBasic(httpBasic ->
+ httpBasic.authenticationManager(customAuthenticationManager)
+ )
+ .build();
+ WebFilterChainProxy springSecurityFilterChain = new WebFilterChainProxy(securityFilterChain);
+ WebTestClient client = WebTestClientBuilder.bindToWebFilters(springSecurityFilterChain).build();
+
+ client.get()
+ .uri("/")
+ .headers(headers -> headers.setBasicAuth("rob", "rob"))
+ .exchange()
+ .expectStatus().isOk()
+ .expectBody(String.class).consumeWith(b -> assertThat(b.getResponseBody()).isEqualTo("ok"));
+
+ verifyZeroInteractions(this.authenticationManager);
+ verify(customAuthenticationManager).authenticate(any(Authentication.class));
+ }
+
@Test
@SuppressWarnings("unchecked")
public void addsX509FilterWhenX509AuthenticationIsConfigured() {
@@ -319,6 +385,23 @@ public class ServerHttpSecurityTests {
assertThat(x509WebFilter).isNotNull();
}
+ @Test
+ public void x509WhenCustomizedThenAddsX509Filter() throws Exception {
+ X509PrincipalExtractor mockExtractor = mock(X509PrincipalExtractor.class);
+ ReactiveAuthenticationManager mockAuthenticationManager = mock(ReactiveAuthenticationManager.class);
+
+ this.http.x509(x509 ->
+ x509
+ .principalExtractor(mockExtractor)
+ .authenticationManager(mockAuthenticationManager)
+ );
+
+ SecurityWebFilterChain securityWebFilterChain = this.http.build();
+ WebFilter x509WebFilter = securityWebFilterChain.getWebFilters().filter(this::isX509Filter).blockFirst();
+
+ assertThat(x509WebFilter).isNotNull();
+ }
+
@Test
public void addsX509FilterWhenX509AuthenticationIsConfiguredWithDefaults() {
this.http.x509();
@@ -329,6 +412,46 @@ public class ServerHttpSecurityTests {
assertThat(x509WebFilter).isNotNull();
}
+ @Test
+ public void x509WhenDefaultsThenAddsX509Filter() throws Exception {
+ this.http.x509(withDefaults());
+
+ SecurityWebFilterChain securityWebFilterChain = this.http.build();
+ WebFilter x509WebFilter = securityWebFilterChain.getWebFilters().filter(this::isX509Filter).blockFirst();
+
+ assertThat(x509WebFilter).isNotNull();
+ }
+
+ @Test
+ public void postWhenCsrfDisabledThenPermitted() throws Exception {
+ SecurityWebFilterChain securityFilterChain = this.http.csrf(csrf -> csrf.disable()).build();
+ WebFilterChainProxy springSecurityFilterChain = new WebFilterChainProxy(securityFilterChain);
+ WebTestClient client = WebTestClientBuilder.bindToWebFilters(springSecurityFilterChain).build();
+
+ client.post()
+ .uri("/")
+ .exchange()
+ .expectStatus().isOk();
+ }
+
+ @Test
+ public void postWhenCustomCsrfTokenRepositoryThenUsed() throws Exception {
+ ServerCsrfTokenRepository customServerCsrfTokenRepository = mock(ServerCsrfTokenRepository.class);
+ when(customServerCsrfTokenRepository.loadToken(any(ServerWebExchange.class))).thenReturn(Mono.empty());
+ SecurityWebFilterChain securityFilterChain = this.http
+ .csrf(csrf -> csrf.csrfTokenRepository(customServerCsrfTokenRepository))
+ .build();
+ WebFilterChainProxy springSecurityFilterChain = new WebFilterChainProxy(securityFilterChain);
+ WebTestClient client = WebTestClientBuilder.bindToWebFilters(springSecurityFilterChain).build();
+
+ client.post()
+ .uri("/")
+ .exchange()
+ .expectStatus().isForbidden();
+
+ verify(customServerCsrfTokenRepository).loadToken(any());
+ }
+
private boolean isX509Filter(WebFilter filter) {
try {
Object converter = ReflectionTestUtils.getField(filter, "authenticationConverter");
diff --git a/docs/manual/src/docs/asciidoc/_includes/reactive/cors.adoc b/docs/manual/src/docs/asciidoc/_includes/reactive/cors.adoc
index 4e62b55a77..968db86d14 100644
--- a/docs/manual/src/docs/asciidoc/_includes/reactive/cors.adoc
+++ b/docs/manual/src/docs/asciidoc/_includes/reactive/cors.adoc
@@ -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();
}
----
diff --git a/docs/manual/src/docs/asciidoc/_includes/reactive/headers.adoc b/docs/manual/src/docs/asciidoc/_includes/reactive/headers.adoc
index 43907c2834..22b886482a 100644
--- a/docs/manual/src/docs/asciidoc/_includes/reactive/headers.adoc
+++ b/docs/manual/src/docs/asciidoc/_includes/reactive/headers.adoc
@@ -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();
}
----
diff --git a/docs/manual/src/docs/asciidoc/_includes/reactive/method.adoc b/docs/manual/src/docs/asciidoc/_includes/reactive/method.adoc
index 531a0d6ce8..fb32f0e14e 100644
--- a/docs/manual/src/docs/asciidoc/_includes/reactive/method.adoc
+++ b/docs/manual/src/docs/asciidoc/_includes/reactive/method.adoc
@@ -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();
}
diff --git a/docs/manual/src/docs/asciidoc/_includes/reactive/oauth2/access-token.adoc b/docs/manual/src/docs/asciidoc/_includes/reactive/oauth2/access-token.adoc
index a58f08c282..cdefccc337 100644
--- a/docs/manual/src/docs/asciidoc/_includes/reactive/oauth2/access-token.adoc
+++ b/docs/manual/src/docs/asciidoc/_includes/reactive/oauth2/access-token.adoc
@@ -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();
}
----
diff --git a/docs/manual/src/docs/asciidoc/_includes/reactive/oauth2/login.adoc b/docs/manual/src/docs/asciidoc/_includes/reactive/oauth2/login.adoc
index 719011752f..fea6fa2629 100644
--- a/docs/manual/src/docs/asciidoc/_includes/reactive/oauth2/login.adoc
+++ b/docs/manual/src/docs/asciidoc/_includes/reactive/oauth2/login.adoc
@@ -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();
}
----
diff --git a/docs/manual/src/docs/asciidoc/_includes/reactive/oauth2/resource-server.adoc b/docs/manual/src/docs/asciidoc/_includes/reactive/oauth2/resource-server.adoc
index 6e7a4667e2..1788668249 100644
--- a/docs/manual/src/docs/asciidoc/_includes/reactive/oauth2/resource-server.adoc
+++ b/docs/manual/src/docs/asciidoc/_includes/reactive/oauth2/resource-server.adoc
@@ -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 <
+ 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> grantedAuthoritiesExtractor() {
diff --git a/docs/manual/src/docs/asciidoc/_includes/reactive/redirect-https.adoc b/docs/manual/src/docs/asciidoc/_includes/reactive/redirect-https.adoc
index 5c142b6209..9e9020d1e5 100644
--- a/docs/manual/src/docs/asciidoc/_includes/reactive/redirect-https.adoc
+++ b/docs/manual/src/docs/asciidoc/_includes/reactive/redirect-https.adoc
@@ -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();
}
----
diff --git a/docs/manual/src/docs/asciidoc/_includes/reactive/webflux.adoc b/docs/manual/src/docs/asciidoc/_includes/reactive/webflux.adoc
index 8752b8311b..03cd665fe8 100644
--- a/docs/manual/src/docs/asciidoc/_includes/reactive/webflux.adoc
+++ b/docs/manual/src/docs/asciidoc/_includes/reactive/webflux.adoc
@@ -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();
}
}
diff --git a/docs/manual/src/docs/asciidoc/_includes/reactive/x509.adoc b/docs/manual/src/docs/asciidoc/_includes/reactive/x509.adoc
index 7caee9091a..211cf3ace7 100644
--- a/docs/manual/src/docs/asciidoc/_includes/reactive/x509.adoc
+++ b/docs/manual/src/docs/asciidoc/_includes/reactive/x509.adoc
@@ -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();
}
----
diff --git a/samples/boot/hellowebflux-method/src/main/java/sample/SecurityConfig.java b/samples/boot/hellowebflux-method/src/main/java/sample/SecurityConfig.java
index e3eb5ae341..bb7349c7dc 100644
--- a/samples/boot/hellowebflux-method/src/main/java/sample/SecurityConfig.java
+++ b/samples/boot/hellowebflux-method/src/main/java/sample/SecurityConfig.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2017 the original author or authors.
+ * Copyright 2002-2019 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.
@@ -25,6 +25,8 @@ import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.web.server.SecurityWebFilterChain;
+import static org.springframework.security.config.Customizer.withDefaults;
+
/**
* @author Rob Winch
* @since 5.0
@@ -38,10 +40,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();
}
diff --git a/samples/boot/oauth2resourceserver-webflux/src/main/java/sample/SecurityConfig.java b/samples/boot/oauth2resourceserver-webflux/src/main/java/sample/SecurityConfig.java
index ba7a373fd0..9c2e54afce 100644
--- a/samples/boot/oauth2resourceserver-webflux/src/main/java/sample/SecurityConfig.java
+++ b/samples/boot/oauth2resourceserver-webflux/src/main/java/sample/SecurityConfig.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2018 the original author or authors.
+ * Copyright 2002-2019 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.
@@ -21,6 +21,8 @@ import org.springframework.security.config.annotation.web.reactive.EnableWebFlux
import org.springframework.security.config.web.server.ServerHttpSecurity;
import org.springframework.security.web.server.SecurityWebFilterChain;
+import static org.springframework.security.config.Customizer.withDefaults;
+
/**
* @author Rob Winch
* @since 5.1
@@ -31,12 +33,15 @@ public class SecurityConfig {
@Bean
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();
}
}
diff --git a/samples/boot/oauth2webclient-webflux/src/main/java/sample/config/SecurityConfig.java b/samples/boot/oauth2webclient-webflux/src/main/java/sample/config/SecurityConfig.java
index aaf2796fea..d2b73b4824 100644
--- a/samples/boot/oauth2webclient-webflux/src/main/java/sample/config/SecurityConfig.java
+++ b/samples/boot/oauth2webclient-webflux/src/main/java/sample/config/SecurityConfig.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2018 the original author or authors.
+ * Copyright 2002-2019 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.
@@ -23,6 +23,8 @@ import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.web.server.SecurityWebFilterChain;
+import static org.springframework.security.config.Customizer.withDefaults;
+
/**
* @author Rob Winch
*/
@@ -32,15 +34,14 @@ public class SecurityConfig {
@Bean
SecurityWebFilterChain configure(ServerHttpSecurity http) throws Exception {
http
- .authorizeExchange()
- .pathMatchers("/", "/public/**").permitAll()
- .anyExchange().authenticated()
- .and()
- .oauth2Login()
- .and()
- .formLogin()
- .and()
- .oauth2Client();
+ .authorizeExchange(exchanges ->
+ exchanges
+ .pathMatchers("/", "/public/**").permitAll()
+ .anyExchange().authenticated()
+ )
+ .oauth2Login(withDefaults())
+ .formLogin(withDefaults())
+ .oauth2Client(withDefaults());
return http.build();
}
diff --git a/samples/boot/webflux-form/src/main/java/sample/WebfluxFormSecurityConfig.java b/samples/boot/webflux-form/src/main/java/sample/WebfluxFormSecurityConfig.java
index a133b734ca..3b291f709c 100644
--- a/samples/boot/webflux-form/src/main/java/sample/WebfluxFormSecurityConfig.java
+++ b/samples/boot/webflux-form/src/main/java/sample/WebfluxFormSecurityConfig.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2017 the original author or authors.
+ * Copyright 2002-2019 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.
@@ -24,6 +24,8 @@ import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.web.server.SecurityWebFilterChain;
+import static org.springframework.security.config.Customizer.withDefaults;
+
/**
* @author Rob Winch
* @since 5.0
@@ -42,15 +44,18 @@ public class WebfluxFormSecurityConfig {
}
@Bean
- SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
+ SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) throws Exception {
http
- .authorizeExchange()
- .pathMatchers("/login").permitAll()
- .anyExchange().authenticated()
- .and()
- .httpBasic().and()
- .formLogin()
- .loginPage("/login");
+ .authorizeExchange(exchanges ->
+ exchanges
+ .pathMatchers("/login").permitAll()
+ .anyExchange().authenticated()
+ )
+ .httpBasic(withDefaults())
+ .formLogin(formLogin ->
+ formLogin
+ .loginPage("/login")
+ );
return http.build();
}
}
diff --git a/samples/boot/webflux-x509/src/main/java/sample/WebfluxX509Application.java b/samples/boot/webflux-x509/src/main/java/sample/WebfluxX509Application.java
index a6871f7c72..b4ce0c0a9a 100644
--- a/samples/boot/webflux-x509/src/main/java/sample/WebfluxX509Application.java
+++ b/samples/boot/webflux-x509/src/main/java/sample/WebfluxX509Application.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2018 the original author or authors.
+ * Copyright 2002-2019 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.
@@ -25,6 +25,8 @@ import org.springframework.security.core.userdetails.ReactiveUserDetailsService;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.web.server.SecurityWebFilterChain;
+import static org.springframework.security.config.Customizer.withDefaults;
+
/**
* @author Alexey Nesterov
* @since 5.2
@@ -40,13 +42,14 @@ public class WebfluxX509Application {
}
@Bean
- public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
+ public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) throws Exception {
// @formatter:off
http
- .x509()
- .and()
- .authorizeExchange()
- .anyExchange().authenticated();
+ .x509(withDefaults())
+ .authorizeExchange(exchanges ->
+ exchanges
+ .anyExchange().authenticated()
+ );
// @formatter:on
return http.build();