Configure sample with Mutual-TLS client certificate-bound access tokens
Issue gh-1560
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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 =
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user