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 5559159fbb..e754f431cc 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 @@ -20,6 +20,7 @@ import static org.springframework.security.web.server.DelegatingServerAuthentica import java.io.IOException; import java.io.PrintWriter; import java.io.StringWriter; +import java.security.interfaces.RSAPublicKey; import java.time.Duration; import java.util.ArrayList; import java.util.Arrays; @@ -54,6 +55,11 @@ import org.springframework.security.oauth2.client.userinfo.DefaultReactiveOAuth2 import org.springframework.security.oauth2.client.userinfo.ReactiveOAuth2UserService; import org.springframework.security.oauth2.client.web.OAuth2AuthorizationRequestRedirectWebFilter; import org.springframework.security.oauth2.client.web.ServerOAuth2LoginAuthenticationTokenConverter; +import org.springframework.security.oauth2.jwt.NimbusReactiveJwtDecoder; +import org.springframework.security.oauth2.jwt.ReactiveJwtDecoder; +import org.springframework.security.oauth2.server.resource.authentication.JwtReactiveAuthenticationManager; +import org.springframework.security.oauth2.server.resource.web.server.BearerTokenServerAuthenticationEntryPoint; +import org.springframework.security.oauth2.server.resource.web.server.ServerBearerTokenAuthenticationConverter; import org.springframework.security.web.server.DelegatingServerAuthenticationEntryPoint; import org.springframework.security.web.server.MatcherSecurityWebFilterChain; import org.springframework.security.web.server.SecurityWebFilterChain; @@ -185,6 +191,8 @@ public class ServerHttpSecurity { private OAuth2LoginSpec oauth2Login; + private OAuth2Spec oauth2; + private LogoutSpec logout = new LogoutSpec(); private LoginPageSpec loginPage = new LoginPageSpec(); @@ -448,6 +456,140 @@ public class ServerHttpSecurity { private OAuth2LoginSpec() {} } + /** + * Configures OAuth2 support. An example configuration is provided below: + * + *
+ * @Bean
+ * public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
+ * http
+ * // ...
+ * .oauth2()
+ * .resourceServer()
+ * .jwt()
+ * .jwkSeturi(jwkSetUri);
+ * return http.build();
+ * }
+ *
+ *
+ * @return the {@link HttpBasicSpec} to customize
+ */
+ public OAuth2Spec oauth2() {
+ if (this.oauth2 == null) {
+ this.oauth2 = new OAuth2Spec();
+ }
+ return this.oauth2;
+ }
+
+ /**
+ * Configures OAuth2 Support
+ *
+ * @since 5.1
+ */
+ public class OAuth2Spec {
+ private ResourceServerSpec resourceServer;
+
+ public ResourceServerSpec resourceServer() {
+ if (this.resourceServer == null) {
+ this.resourceServer = new ResourceServerSpec();
+ }
+ return this.resourceServer;
+ }
+
+ /**
+ * Configures OAuth2 Resource Server Support
+ */
+ public class ResourceServerSpec {
+ private JwtSpec jwt;
+
+ public JwtSpec jwt() {
+ if (this.jwt == null) {
+ this.jwt = new JwtSpec();
+ }
+ return this.jwt;
+ }
+
+ protected void configure(ServerHttpSecurity http) {
+ if (this.jwt != null) {
+ this.jwt.configure(http);
+ }
+ }
+
+ /**
+ * Configures JWT Resource Server Support
+ */
+ public class JwtSpec {
+ private ReactiveJwtDecoder jwtDecoder;
+
+ /**
+ * Configures the {@link ReactiveJwtDecoder} to use
+ * @param jwtDecoder the decoder to use
+ * @return the {@code JwtSpec} for additional configuration
+ */
+ public JwtSpec jwtDecoder(ReactiveJwtDecoder jwtDecoder) {
+ this.jwtDecoder = jwtDecoder;
+ return this;
+ }
+
+ /**
+ * Configures a {@link ReactiveJwtDecoder} that leverages the provided {@link RSAPublicKey}
+ *
+ * @param publicKey the public key to use.
+ * @return the {@code JwtSpec} for additional configuration
+ */
+ public JwtSpec publicKey(RSAPublicKey publicKey) {
+ this.jwtDecoder = new NimbusReactiveJwtDecoder(publicKey);
+ return this;
+ }
+
+ /**
+ * Configures a {@link ReactiveJwtDecoder} using
+ * JSON Web Key (JWK) URL
+ * @param jwkSetUri the URL to use.
+ * @return the {@code JwtSpec} for additional configuration
+ */
+ public JwtSpec jwkSetUri(String jwkSetUri) {
+ this.jwtDecoder = new NimbusReactiveJwtDecoder(jwkSetUri);
+ return this;
+ }
+
+ public ResourceServerSpec and() {
+ return ResourceServerSpec.this;
+ }
+
+ protected void configure(ServerHttpSecurity http) {
+ BearerTokenServerAuthenticationEntryPoint entryPoint = new BearerTokenServerAuthenticationEntryPoint();
+ JwtReactiveAuthenticationManager authenticationManager = new JwtReactiveAuthenticationManager(
+ this.jwtDecoder);
+ AuthenticationWebFilter oauth2 = new AuthenticationWebFilter(authenticationManager);
+ oauth2.setAuthenticationConverter(new ServerBearerTokenAuthenticationConverter());
+ oauth2.setAuthenticationFailureHandler(new ServerAuthenticationEntryPointFailureHandler(entryPoint));
+ http
+ .exceptionHandling()
+ .authenticationEntryPoint(entryPoint)
+ .and()
+ .addFilterAt(oauth2, SecurityWebFiltersOrder.AUTHENTICATION);
+ }
+ }
+
+ public OAuth2Spec and() {
+ return OAuth2Spec.this;
+ }
+ }
+
+ public ServerHttpSecurity and() {
+ return ServerHttpSecurity.this;
+ }
+
+ protected void configure(ServerHttpSecurity http) {
+ if (this.resourceServer != null) {
+ this.resourceServer.configure(http);
+ }
+ }
+
+ private OAuth2Spec() {}
+ }
+
/**
* Configures HTTP Response Headers. The default headers are:
*
@@ -645,6 +787,9 @@ public class ServerHttpSecurity {
if (this.oauth2Login != null) {
this.oauth2Login.configure(this);
}
+ if (this.oauth2 != null) {
+ this.oauth2.configure(this);
+ }
this.loginPage.configure(this);
if(this.logout != null) {
this.logout.configure(this);