Bläddra i källkod

Configure sample with Mutual-TLS client certificate-bound access tokens

Issue gh-1560
Joe Grandja 1 år sedan
förälder
incheckning
ff4b542b83

+ 6 - 0
samples/demo-authorizationserver/src/main/java/sample/config/AuthorizationServerConfig.java

@@ -50,6 +50,7 @@ import org.springframework.security.oauth2.server.authorization.config.annotatio
 import org.springframework.security.oauth2.server.authorization.config.annotation.web.configurers.OAuth2AuthorizationServerConfigurer;
 import org.springframework.security.oauth2.server.authorization.settings.AuthorizationServerSettings;
 import org.springframework.security.oauth2.server.authorization.settings.ClientSettings;
+import org.springframework.security.oauth2.server.authorization.settings.TokenSettings;
 import org.springframework.security.oauth2.server.authorization.token.JwtEncodingContext;
 import org.springframework.security.oauth2.server.authorization.token.OAuth2TokenCustomizer;
 import org.springframework.security.web.SecurityFilterChain;
@@ -179,6 +180,11 @@ public class AuthorizationServerConfig {
 								.jwkSetUrl("http://127.0.0.1:8080/jwks")
 								.build()
 				)
+				.tokenSettings(
+						TokenSettings.builder()
+								.x509CertificateBoundAccessTokens(true)
+								.build()
+				)
 				.build();
 
 		// Save registered client's in db as if in-memory

+ 35 - 2
samples/demo-client/src/main/java/sample/config/WebClientConfig.java

@@ -18,13 +18,24 @@ package sample.config;
 import java.util.Arrays;
 import java.util.function.Supplier;
 
+import javax.net.ssl.KeyManagerFactory;
+import javax.net.ssl.TrustManagerFactory;
+
+import io.netty.handler.ssl.SslContext;
+import io.netty.handler.ssl.SslContextBuilder;
+import reactor.netty.http.client.HttpClient;
+import reactor.netty.tcp.SslProvider;
 import sample.authorization.DeviceCodeOAuth2AuthorizedClientProvider;
 
 import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.boot.ssl.SslBundle;
+import org.springframework.boot.ssl.SslBundles;
 import org.springframework.boot.web.client.RestTemplateBuilder;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
 import org.springframework.http.client.ClientHttpRequestFactory;
+import org.springframework.http.client.reactive.ClientHttpConnector;
+import org.springframework.http.client.reactive.ReactorClientHttpConnector;
 import org.springframework.http.converter.FormHttpMessageConverter;
 import org.springframework.security.oauth2.client.OAuth2AuthorizedClientManager;
 import org.springframework.security.oauth2.client.OAuth2AuthorizedClientProvider;
@@ -54,11 +65,15 @@ import org.springframework.web.reactive.function.client.WebClient;
 public class WebClientConfig {
 
 	@Bean("default-client-web-client")
-	public WebClient defaultClientWebClient(OAuth2AuthorizedClientManager authorizedClientManager) {
+	public WebClient defaultClientWebClient(
+			OAuth2AuthorizedClientManager authorizedClientManager,
+			SslBundles sslBundles) throws Exception {
+
 		ServletOAuth2AuthorizedClientExchangeFilterFunction oauth2Client =
 				new ServletOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager);
 		// @formatter:off
 		return WebClient.builder()
+				.clientConnector(createClientConnector(sslBundles.getBundle("demo-client")))
 				.apply(oauth2Client.oauth2Configuration())
 				.build();
 		// @formatter:on
@@ -69,7 +84,8 @@ public class WebClientConfig {
 			ClientRegistrationRepository clientRegistrationRepository,
 			OAuth2AuthorizedClientRepository authorizedClientRepository,
 			RestTemplateBuilder restTemplateBuilder,
-			@Qualifier("self-signed-demo-client-http-request-factory") Supplier<ClientHttpRequestFactory> clientHttpRequestFactory) {
+			@Qualifier("self-signed-demo-client-http-request-factory") Supplier<ClientHttpRequestFactory> clientHttpRequestFactory,
+			SslBundles sslBundles) throws Exception {
 
 		// @formatter:off
 		RestTemplate restTemplate = restTemplateBuilder
@@ -98,6 +114,7 @@ public class WebClientConfig {
 				new ServletOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager);
 		// @formatter:off
 		return WebClient.builder()
+				.clientConnector(createClientConnector(sslBundles.getBundle("self-signed-demo-client")))
 				.apply(oauth2Client.oauth2Configuration())
 				.build();
 		// @formatter:on
@@ -143,6 +160,22 @@ public class WebClientConfig {
 		return authorizedClientManager;
 	}
 
+	private static ClientHttpConnector createClientConnector(SslBundle sslBundle) throws Exception {
+		KeyManagerFactory keyManagerFactory = sslBundle.getManagers().getKeyManagerFactory();
+		TrustManagerFactory trustManagerFactory = sslBundle.getManagers().getTrustManagerFactory();
+
+		// @formatter:off
+		SslContext sslContext = SslContextBuilder.forClient()
+				.keyManager(keyManagerFactory)
+				.trustManager(trustManagerFactory)
+				.build();
+		// @formatter:on
+
+		SslProvider sslProvider = SslProvider.builder().sslContext(sslContext).build();
+		HttpClient httpClient = HttpClient.create().secure(sslProvider);
+		return new ReactorClientHttpConnector(httpClient);
+	}
+
 	private static OAuth2AccessTokenResponseClient<OAuth2ClientCredentialsGrantRequest> createClientCredentialsTokenResponseClient(
 			RestTemplate restTemplate) {
 		DefaultClientCredentialsTokenResponseClient clientCredentialsTokenResponseClient =

+ 1 - 1
samples/demo-client/src/main/resources/application.yml

@@ -101,7 +101,7 @@ spring:
             token-uri: https://localhost:9443/oauth2/token
 
 messages:
-  base-uri: http://127.0.0.1:8090/messages
+  base-uri: https://127.0.0.1:8443/messages
 
 user-messages:
   base-uri: http://127.0.0.1:8091/user/messages

+ 8 - 0
samples/messages-resource/src/main/java/sample/config/ResourceServerConfig.java

@@ -30,6 +30,14 @@ import org.springframework.security.web.SecurityFilterChain;
 @Configuration(proxyBeanMethods = false)
 public class ResourceServerConfig {
 
+	/*
+		NOTE:
+		The `NimbusJwtDecoder` `@Bean` autoconfigured by Spring Boot will contain
+		an `OAuth2TokenValidator<Jwt>` of type `X509CertificateThumbprintValidator`.
+		This is the validator responsible for validating the `x5t#S256` claim (if available)
+		in the `Jwt` against the SHA-256 Thumbprint of the supplied `X509Certificate`.
+	 */
+
 	// @formatter:off
 	@Bean
 	SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {

+ 46 - 0
samples/messages-resource/src/main/java/sample/config/TomcatServerConfig.java

@@ -0,0 +1,46 @@
+/*
+ * Copyright 2020-2024 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package sample.config;
+
+import org.apache.catalina.connector.Connector;
+
+import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
+import org.springframework.boot.web.server.WebServerFactoryCustomizer;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * @author Joe Grandja
+ * @since 1.3
+ */
+@Configuration(proxyBeanMethods = false)
+public class TomcatServerConfig {
+
+	@Bean
+	public WebServerFactoryCustomizer<TomcatServletWebServerFactory> connectorCustomizer() {
+		return (tomcat) -> tomcat.addAdditionalTomcatConnectors(createHttpConnector());
+	}
+
+	private Connector createHttpConnector() {
+		Connector connector = new Connector(TomcatServletWebServerFactory.DEFAULT_PROTOCOL);
+		connector.setScheme("http");
+		connector.setPort(8090);
+		connector.setSecure(false);
+		connector.setRedirectPort(8443);
+		return connector;
+	}
+
+}

+ 19 - 1
samples/messages-resource/src/main/resources/application.yml

@@ -1,5 +1,8 @@
 server:
-  port: 8090
+  port: 8443
+  ssl:
+    bundle: messages-resource
+    client-auth: need
 
 logging:
   level:
@@ -10,6 +13,21 @@ logging:
 #    org.springframework.boot.autoconfigure: DEBUG
 
 spring:
+  ssl:
+    bundle:
+      jks:
+        messages-resource:
+          key:
+            alias: messages-resource-sample
+            password: password
+          keystore:
+            location: classpath:keystore.p12
+            password: password
+            type: PKCS12
+          truststore:
+            location: classpath:keystore.p12
+            password: password
+            type: PKCS12
   security:
     oauth2:
       resourceserver: