diff --git a/samples/default-authorizationserver/samples-default-authorizationserver.gradle b/samples/default-authorizationserver/samples-default-authorizationserver.gradle index 02b38821..0cabf5fd 100644 --- a/samples/default-authorizationserver/samples-default-authorizationserver.gradle +++ b/samples/default-authorizationserver/samples-default-authorizationserver.gradle @@ -20,6 +20,7 @@ repositories { dependencies { implementation "org.springframework.boot:spring-boot-starter-web" implementation "org.springframework.boot:spring-boot-starter-security" + implementation "org.springframework.boot:spring-boot-starter-oauth2-authorization-server" implementation project(":spring-security-oauth2-authorization-server") testImplementation "org.springframework.boot:spring-boot-starter-test" diff --git a/samples/demo-authorizationserver/src/main/java/sample/config/TomcatServerConfig.java b/samples/demo-authorizationserver/src/main/java/sample/config/TomcatServerConfig.java index 59e7537d..20b31c52 100644 --- a/samples/demo-authorizationserver/src/main/java/sample/config/TomcatServerConfig.java +++ b/samples/demo-authorizationserver/src/main/java/sample/config/TomcatServerConfig.java @@ -1,5 +1,5 @@ /* - * Copyright 2020-2024 the original author or authors. + * Copyright 2020-2025 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. @@ -17,7 +17,7 @@ package sample.config; import org.apache.catalina.connector.Connector; -import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory; +import org.springframework.boot.tomcat.servlet.TomcatServletWebServerFactory; import org.springframework.boot.web.server.WebServerFactoryCustomizer; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -33,7 +33,7 @@ public class TomcatServerConfig { @Bean public WebServerFactoryCustomizer connectorCustomizer() { - return (tomcat) -> tomcat.addAdditionalTomcatConnectors(createHttpConnector()); + return (tomcat) -> tomcat.addAdditionalConnectors(createHttpConnector()); } private Connector createHttpConnector() { diff --git a/samples/demo-authorizationserver/src/main/java/sample/web/DefaultErrorController.java b/samples/demo-authorizationserver/src/main/java/sample/web/DefaultErrorController.java index af4a3c06..7b65b1ab 100644 --- a/samples/demo-authorizationserver/src/main/java/sample/web/DefaultErrorController.java +++ b/samples/demo-authorizationserver/src/main/java/sample/web/DefaultErrorController.java @@ -1,5 +1,5 @@ /* - * Copyright 2020-2023 the original author or authors. + * Copyright 2020-2025 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. @@ -18,7 +18,7 @@ package sample.web; import jakarta.servlet.RequestDispatcher; import jakarta.servlet.http.HttpServletRequest; -import org.springframework.boot.web.servlet.error.ErrorController; +import org.springframework.boot.webmvc.error.ErrorController; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.util.StringUtils; diff --git a/samples/demo-client/src/main/java/sample/authorization/OAuth2DeviceAccessTokenResponseClient.java b/samples/demo-client/src/main/java/sample/authorization/OAuth2DeviceAccessTokenResponseClient.java index 2c3486ff..f27588f7 100644 --- a/samples/demo-client/src/main/java/sample/authorization/OAuth2DeviceAccessTokenResponseClient.java +++ b/samples/demo-client/src/main/java/sample/authorization/OAuth2DeviceAccessTokenResponseClient.java @@ -1,5 +1,5 @@ /* - * Copyright 2020-2023 the original author or authors. + * Copyright 2020-2025 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. @@ -15,10 +15,7 @@ */ package sample.authorization; -import java.util.Arrays; - import org.springframework.http.HttpHeaders; -import org.springframework.http.RequestEntity; import org.springframework.http.converter.FormHttpMessageConverter; import org.springframework.security.oauth2.client.endpoint.OAuth2AccessTokenResponseClient; import org.springframework.security.oauth2.client.http.OAuth2ErrorResponseErrorHandler; @@ -31,9 +28,8 @@ import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames; import org.springframework.security.oauth2.core.http.converter.OAuth2AccessTokenResponseHttpMessageConverter; import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; +import org.springframework.web.client.RestClient; import org.springframework.web.client.RestClientException; -import org.springframework.web.client.RestOperations; -import org.springframework.web.client.RestTemplate; /** * @author Steve Riesenberg @@ -41,24 +37,28 @@ import org.springframework.web.client.RestTemplate; */ public final class OAuth2DeviceAccessTokenResponseClient implements OAuth2AccessTokenResponseClient { - private RestOperations restOperations; + private RestClient restClient; public OAuth2DeviceAccessTokenResponseClient() { - RestTemplate restTemplate = new RestTemplate(Arrays.asList(new FormHttpMessageConverter(), - new OAuth2AccessTokenResponseHttpMessageConverter())); - restTemplate.setErrorHandler(new OAuth2ErrorResponseErrorHandler()); - this.restOperations = restTemplate; + this.restClient = RestClient.builder() + .messageConverters((messageConverters) -> { + messageConverters.clear(); + messageConverters.add(new FormHttpMessageConverter()); + messageConverters.add(new OAuth2AccessTokenResponseHttpMessageConverter()); + }) + .defaultStatusHandler(new OAuth2ErrorResponseErrorHandler()) + .build(); } - public void setRestOperations(RestOperations restOperations) { - this.restOperations = restOperations; + public void setRestClient(RestClient restClient) { + this.restClient = restClient; } @Override public OAuth2AccessTokenResponse getTokenResponse(OAuth2DeviceGrantRequest deviceGrantRequest) { ClientRegistration clientRegistration = deviceGrantRequest.getClientRegistration(); - HttpHeaders headers = new HttpHeaders(); + HttpHeaders headerParameters = new HttpHeaders(); /* * This sample demonstrates the use of a public client that does not * store credentials or authenticate with the authorization server. @@ -71,7 +71,7 @@ public final class OAuth2DeviceAccessTokenResponseClient implements OAuth2Access * OAuth 2.0 Token Request with a clientId/clientSecret. */ if (!clientRegistration.getClientAuthenticationMethod().equals(ClientAuthenticationMethod.NONE)) { - headers.setBasicAuth(clientRegistration.getClientId(), clientRegistration.getClientSecret()); + headerParameters.setBasicAuth(clientRegistration.getClientId(), clientRegistration.getClientSecret()); } MultiValueMap requestParameters = new LinkedMultiValueMap<>(); @@ -79,15 +79,15 @@ public final class OAuth2DeviceAccessTokenResponseClient implements OAuth2Access requestParameters.add(OAuth2ParameterNames.CLIENT_ID, clientRegistration.getClientId()); requestParameters.add(OAuth2ParameterNames.DEVICE_CODE, deviceGrantRequest.getDeviceCode()); - // @formatter:off - RequestEntity> requestEntity = - RequestEntity.post(deviceGrantRequest.getClientRegistration().getProviderDetails().getTokenUri()) - .headers(headers) - .body(requestParameters); - // @formatter:on - try { - return this.restOperations.exchange(requestEntity, OAuth2AccessTokenResponse.class).getBody(); + // @formatter:off + return this.restClient.post() + .uri(deviceGrantRequest.getClientRegistration().getProviderDetails().getTokenUri()) + .headers((headers) -> headers.putAll(headerParameters)) + .body(requestParameters) + .retrieve() + .body(OAuth2AccessTokenResponse.class); + // @formatter:on } catch (RestClientException ex) { OAuth2Error oauth2Error = new OAuth2Error("invalid_token_response", "An error occurred while attempting to retrieve the OAuth 2.0 Access Token Response: " diff --git a/samples/demo-client/src/main/java/sample/config/RestTemplateConfig.java b/samples/demo-client/src/main/java/sample/config/RestClientConfig.java similarity index 97% rename from samples/demo-client/src/main/java/sample/config/RestTemplateConfig.java rename to samples/demo-client/src/main/java/sample/config/RestClientConfig.java index 052e4e45..32f2430c 100644 --- a/samples/demo-client/src/main/java/sample/config/RestTemplateConfig.java +++ b/samples/demo-client/src/main/java/sample/config/RestClientConfig.java @@ -1,5 +1,5 @@ /* - * Copyright 2020-2024 the original author or authors. + * Copyright 2020-2025 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. @@ -41,7 +41,7 @@ import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; * @since 1.3 */ @Configuration(proxyBeanMethods = false) -public class RestTemplateConfig { +public class RestClientConfig { @Bean("default-client-http-request-factory") Supplier defaultClientHttpRequestFactory(SslBundles sslBundles) { diff --git a/samples/demo-client/src/main/java/sample/config/WebClientConfig.java b/samples/demo-client/src/main/java/sample/config/WebClientConfig.java index 8722d8c8..9347d3d8 100644 --- a/samples/demo-client/src/main/java/sample/config/WebClientConfig.java +++ b/samples/demo-client/src/main/java/sample/config/WebClientConfig.java @@ -1,5 +1,5 @@ /* - * Copyright 2020-2024 the original author or authors. + * Copyright 2020-2025 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. @@ -15,7 +15,6 @@ */ package sample.config; -import java.util.Arrays; import java.util.function.Supplier; import javax.net.ssl.KeyManagerFactory; @@ -30,7 +29,6 @@ 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; @@ -40,10 +38,9 @@ import org.springframework.http.converter.FormHttpMessageConverter; import org.springframework.security.oauth2.client.OAuth2AuthorizedClientManager; import org.springframework.security.oauth2.client.OAuth2AuthorizedClientProvider; import org.springframework.security.oauth2.client.OAuth2AuthorizedClientProviderBuilder; -import org.springframework.security.oauth2.client.endpoint.DefaultClientCredentialsTokenResponseClient; import org.springframework.security.oauth2.client.endpoint.OAuth2AccessTokenResponseClient; import org.springframework.security.oauth2.client.endpoint.OAuth2ClientCredentialsGrantRequest; -import org.springframework.security.oauth2.client.endpoint.OAuth2ClientCredentialsGrantRequestEntityConverter; +import org.springframework.security.oauth2.client.endpoint.RestClientClientCredentialsTokenResponseClient; import org.springframework.security.oauth2.client.http.OAuth2ErrorResponseErrorHandler; import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository; import org.springframework.security.oauth2.client.web.DefaultOAuth2AuthorizedClientManager; @@ -53,7 +50,7 @@ import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames; import org.springframework.security.oauth2.core.http.converter.OAuth2AccessTokenResponseHttpMessageConverter; import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; -import org.springframework.web.client.RestTemplate; +import org.springframework.web.client.RestClient; import org.springframework.web.reactive.function.client.WebClient; /** @@ -83,17 +80,18 @@ public class WebClientConfig { public WebClient selfSignedDemoClientWebClient( ClientRegistrationRepository clientRegistrationRepository, OAuth2AuthorizedClientRepository authorizedClientRepository, - RestTemplateBuilder restTemplateBuilder, @Qualifier("self-signed-demo-client-http-request-factory") Supplier clientHttpRequestFactory, SslBundles sslBundles) throws Exception { // @formatter:off - RestTemplate restTemplate = restTemplateBuilder - .requestFactory(clientHttpRequestFactory) - .messageConverters(Arrays.asList( - new FormHttpMessageConverter(), - new OAuth2AccessTokenResponseHttpMessageConverter())) - .errorHandler(new OAuth2ErrorResponseErrorHandler()) + RestClient restClient = RestClient.builder() + .requestFactory(clientHttpRequestFactory.get()) + .messageConverters((messageConverters) -> { + messageConverters.clear(); + messageConverters.add(new FormHttpMessageConverter()); + messageConverters.add(new OAuth2AccessTokenResponseHttpMessageConverter()); + }) + .defaultStatusHandler(new OAuth2ErrorResponseErrorHandler()) .build(); // @formatter:on @@ -102,7 +100,7 @@ public class WebClientConfig { OAuth2AuthorizedClientProviderBuilder.builder() .clientCredentials(clientCredentials -> clientCredentials.accessTokenResponseClient( - createClientCredentialsTokenResponseClient(restTemplate))) + createClientCredentialsTokenResponseClient(restClient))) .build(); // @formatter:on @@ -124,16 +122,17 @@ public class WebClientConfig { public OAuth2AuthorizedClientManager authorizedClientManager( ClientRegistrationRepository clientRegistrationRepository, OAuth2AuthorizedClientRepository authorizedClientRepository, - RestTemplateBuilder restTemplateBuilder, @Qualifier("default-client-http-request-factory") Supplier clientHttpRequestFactory) { // @formatter:off - RestTemplate restTemplate = restTemplateBuilder - .requestFactory(clientHttpRequestFactory) - .messageConverters(Arrays.asList( - new FormHttpMessageConverter(), - new OAuth2AccessTokenResponseHttpMessageConverter())) - .errorHandler(new OAuth2ErrorResponseErrorHandler()) + RestClient restClient = RestClient.builder() + .requestFactory(clientHttpRequestFactory.get()) + .messageConverters((messageConverters) -> { + messageConverters.clear(); + messageConverters.add(new FormHttpMessageConverter()); + messageConverters.add(new OAuth2AccessTokenResponseHttpMessageConverter()); + }) + .defaultStatusHandler(new OAuth2ErrorResponseErrorHandler()) .build(); // @formatter:on @@ -144,7 +143,7 @@ public class WebClientConfig { .refreshToken() .clientCredentials(clientCredentials -> clientCredentials.accessTokenResponseClient( - createClientCredentialsTokenResponseClient(restTemplate))) + createClientCredentialsTokenResponseClient(restClient))) .provider(new DeviceCodeOAuth2AuthorizedClientProvider()) .build(); // @formatter:on @@ -177,20 +176,16 @@ public class WebClientConfig { } private static OAuth2AccessTokenResponseClient createClientCredentialsTokenResponseClient( - RestTemplate restTemplate) { - DefaultClientCredentialsTokenResponseClient clientCredentialsTokenResponseClient = - new DefaultClientCredentialsTokenResponseClient(); - clientCredentialsTokenResponseClient.setRestOperations(restTemplate); - - OAuth2ClientCredentialsGrantRequestEntityConverter clientCredentialsGrantRequestEntityConverter = - new OAuth2ClientCredentialsGrantRequestEntityConverter(); - clientCredentialsGrantRequestEntityConverter.addParametersConverter(authorizationGrantRequest -> { + RestClient restClient) { + RestClientClientCredentialsTokenResponseClient clientCredentialsTokenResponseClient = + new RestClientClientCredentialsTokenResponseClient(); + clientCredentialsTokenResponseClient.addParametersConverter(authorizationGrantRequest -> { MultiValueMap parameters = new LinkedMultiValueMap<>(); // client_id parameter is required for tls_client_auth method parameters.add(OAuth2ParameterNames.CLIENT_ID, authorizationGrantRequest.getClientRegistration().getClientId()); return parameters; }); - clientCredentialsTokenResponseClient.setRequestEntityConverter(clientCredentialsGrantRequestEntityConverter); + clientCredentialsTokenResponseClient.setRestClient(restClient); return clientCredentialsTokenResponseClient; } diff --git a/samples/messages-resource/src/main/java/sample/config/TomcatServerConfig.java b/samples/messages-resource/src/main/java/sample/config/TomcatServerConfig.java index 54121986..1a65891c 100644 --- a/samples/messages-resource/src/main/java/sample/config/TomcatServerConfig.java +++ b/samples/messages-resource/src/main/java/sample/config/TomcatServerConfig.java @@ -1,5 +1,5 @@ /* - * Copyright 2020-2024 the original author or authors. + * Copyright 2020-2025 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. @@ -17,7 +17,7 @@ package sample.config; import org.apache.catalina.connector.Connector; -import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory; +import org.springframework.boot.tomcat.servlet.TomcatServletWebServerFactory; import org.springframework.boot.web.server.WebServerFactoryCustomizer; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -31,7 +31,7 @@ public class TomcatServerConfig { @Bean public WebServerFactoryCustomizer connectorCustomizer() { - return (tomcat) -> tomcat.addAdditionalTomcatConnectors(createHttpConnector()); + return (tomcat) -> tomcat.addAdditionalConnectors(createHttpConnector()); } private Connector createHttpConnector() {