diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/client/SsoSecurityConfigurer.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/client/SsoSecurityConfigurer.java index f3786abed67..b9ce9779167 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/client/SsoSecurityConfigurer.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/client/SsoSecurityConfigurer.java @@ -18,6 +18,7 @@ package org.springframework.boot.autoconfigure.security.oauth2.client; import java.util.Collections; +import org.springframework.boot.autoconfigure.security.oauth2.resource.UserInfoRestTemplateConfiguration; import org.springframework.context.ApplicationContext; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; @@ -83,7 +84,7 @@ class SsoSecurityConfigurer { private OAuth2ClientAuthenticationProcessingFilter oauth2SsoFilter( OAuth2SsoProperties sso) { OAuth2RestOperations restTemplate = this.applicationContext - .getBean(OAuth2RestOperations.class); + .getBean(UserInfoRestTemplateConfiguration.class).userInfoRestTemplate(); ResourceServerTokenServices tokenServices = this.applicationContext .getBean(ResourceServerTokenServices.class); OAuth2ClientAuthenticationProcessingFilter filter = new OAuth2ClientAuthenticationProcessingFilter( diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/ResourceServerTokenServicesConfiguration.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/ResourceServerTokenServicesConfiguration.java index 1f29a0e9903..2b154b27cea 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/ResourceServerTokenServicesConfiguration.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/ResourceServerTokenServicesConfiguration.java @@ -25,7 +25,6 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.beans.factory.ObjectProvider; -import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.autoconfigure.condition.ConditionOutcome; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; @@ -37,6 +36,7 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ConditionContext; import org.springframework.context.annotation.Conditional; import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; import org.springframework.core.annotation.AnnotationAwareOrderComparator; import org.springframework.core.env.Environment; import org.springframework.core.type.AnnotatedTypeMetadata; @@ -49,14 +49,10 @@ import org.springframework.http.client.ClientHttpRequestExecution; import org.springframework.http.client.ClientHttpRequestInterceptor; import org.springframework.http.client.ClientHttpResponse; import org.springframework.security.crypto.codec.Base64; -import org.springframework.security.oauth2.client.OAuth2ClientContext; import org.springframework.security.oauth2.client.OAuth2RestOperations; -import org.springframework.security.oauth2.client.OAuth2RestTemplate; import org.springframework.security.oauth2.client.resource.OAuth2ProtectedResourceDetails; import org.springframework.security.oauth2.client.token.AccessTokenRequest; import org.springframework.security.oauth2.client.token.RequestEnhancer; -import org.springframework.security.oauth2.client.token.grant.code.AuthorizationCodeAccessTokenProvider; -import org.springframework.security.oauth2.client.token.grant.code.AuthorizationCodeResourceDetails; import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerEndpointsConfiguration; import org.springframework.security.oauth2.provider.token.DefaultTokenServices; import org.springframework.security.oauth2.provider.token.RemoteTokenServices; @@ -80,65 +76,12 @@ import org.springframework.web.client.RestTemplate; */ @Configuration @ConditionalOnMissingBean(AuthorizationServerEndpointsConfiguration.class) +@Import(UserInfoRestTemplateConfiguration.class) public class ResourceServerTokenServicesConfiguration { private static final Log logger = LogFactory .getLog(ResourceServerTokenServicesConfiguration.class); - @Configuration - protected static class UserInfoRestTemplateConfiguration { - - private static final AuthorizationCodeResourceDetails DEFAULT_RESOURCE_DETAILS = new AuthorizationCodeResourceDetails(); - - static { - DEFAULT_RESOURCE_DETAILS.setClientId(""); - DEFAULT_RESOURCE_DETAILS - .setUserAuthorizationUri("Not a URI " + "because there is no client"); - DEFAULT_RESOURCE_DETAILS - .setAccessTokenUri("Not a URI " + "because there is no client"); - } - - private final List customizers; - - private final OAuth2ProtectedResourceDetails details; - - private final OAuth2ClientContext oauth2ClientContext; - - public UserInfoRestTemplateConfiguration( - ObjectProvider> customizersProvider, - ObjectProvider detailsProvider, - ObjectProvider oauth2ClientContextProvider) { - this.customizers = customizersProvider.getIfAvailable(); - this.details = detailsProvider.getIfAvailable(); - this.oauth2ClientContext = oauth2ClientContextProvider.getIfAvailable(); - } - - @Bean(name = "userInfoRestTemplate") - public OAuth2RestTemplate userInfoRestTemplate() { - OAuth2RestTemplate template = getTemplate( - this.details == null ? DEFAULT_RESOURCE_DETAILS : this.details); - template.getInterceptors().add(new AcceptJsonRequestInterceptor()); - AuthorizationCodeAccessTokenProvider accessTokenProvider = new AuthorizationCodeAccessTokenProvider(); - accessTokenProvider.setTokenRequestEnhancer(new AcceptJsonRequestEnhancer()); - template.setAccessTokenProvider(accessTokenProvider); - if (!CollectionUtils.isEmpty(this.customizers)) { - AnnotationAwareOrderComparator.sort(this.customizers); - for (UserInfoRestTemplateCustomizer customizer : this.customizers) { - customizer.customize(template); - } - } - return template; - } - - private OAuth2RestTemplate getTemplate(OAuth2ProtectedResourceDetails details) { - if (this.oauth2ClientContext == null) { - return new OAuth2RestTemplate(details); - } - return new OAuth2RestTemplate(details, this.oauth2ClientContext); - } - - } - @Configuration @Conditional(NotJwtTokenCondition.class) protected static class RemoteTokenServicesConfiguration { @@ -179,11 +122,11 @@ public class ResourceServerTokenServicesConfiguration { public SocialTokenServicesConfiguration(ResourceServerProperties sso, ObjectProvider> connectionFactoryProvider, - @Qualifier("userInfoRestTemplate") ObjectProvider restTemplateProvider, + UserInfoRestTemplateConfiguration restTemplateProvider, ObjectProvider authoritiesExtractorProvider) { this.sso = sso; this.connectionFactory = connectionFactoryProvider.getIfAvailable(); - this.restTemplate = restTemplateProvider.getIfAvailable(); + this.restTemplate = restTemplateProvider.userInfoRestTemplate(); this.authoritiesExtractor = authoritiesExtractorProvider.getIfAvailable(); } @@ -223,10 +166,10 @@ public class ResourceServerTokenServicesConfiguration { private final AuthoritiesExtractor authoritiesExtractor; public UserInfoTokenServicesConfiguration(ResourceServerProperties sso, - @Qualifier("userInfoRestTemplate") ObjectProvider restTemplateProvider, + UserInfoRestTemplateConfiguration restTemplateProvider, ObjectProvider authoritiesExtractorProvider) { this.sso = sso; - this.restTemplate = restTemplateProvider.getIfAvailable(); + this.restTemplate = restTemplateProvider.userInfoRestTemplate(); this.authoritiesExtractor = authoritiesExtractorProvider.getIfAvailable(); } @@ -393,8 +336,7 @@ public class ResourceServerTokenServicesConfiguration { } - private static class AcceptJsonRequestInterceptor - implements ClientHttpRequestInterceptor { + static class AcceptJsonRequestInterceptor implements ClientHttpRequestInterceptor { @Override public ClientHttpResponse intercept(HttpRequest request, byte[] body, @@ -405,7 +347,7 @@ public class ResourceServerTokenServicesConfiguration { } - private static class AcceptJsonRequestEnhancer implements RequestEnhancer { + static class AcceptJsonRequestEnhancer implements RequestEnhancer { @Override public void enhance(AccessTokenRequest request, diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/UserInfoRestTemplateConfiguration.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/UserInfoRestTemplateConfiguration.java new file mode 100644 index 00000000000..94b4677355a --- /dev/null +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/UserInfoRestTemplateConfiguration.java @@ -0,0 +1,92 @@ +/* + * Copyright 2012-2016 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 + * + * http://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 org.springframework.boot.autoconfigure.security.oauth2.resource; + +import java.util.List; + +import org.springframework.beans.factory.ObjectProvider; +import org.springframework.boot.autoconfigure.security.oauth2.resource.ResourceServerTokenServicesConfiguration.AcceptJsonRequestEnhancer; +import org.springframework.boot.autoconfigure.security.oauth2.resource.ResourceServerTokenServicesConfiguration.AcceptJsonRequestInterceptor; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.annotation.AnnotationAwareOrderComparator; +import org.springframework.security.oauth2.client.OAuth2ClientContext; +import org.springframework.security.oauth2.client.OAuth2RestTemplate; +import org.springframework.security.oauth2.client.resource.OAuth2ProtectedResourceDetails; +import org.springframework.security.oauth2.client.token.grant.code.AuthorizationCodeAccessTokenProvider; +import org.springframework.security.oauth2.client.token.grant.code.AuthorizationCodeResourceDetails; +import org.springframework.util.CollectionUtils; + +/** + * @author Dave Syer + */ +@Configuration +public class UserInfoRestTemplateConfiguration { + + private static final AuthorizationCodeResourceDetails DEFAULT_RESOURCE_DETAILS = new AuthorizationCodeResourceDetails(); + + static { + DEFAULT_RESOURCE_DETAILS.setClientId(""); + DEFAULT_RESOURCE_DETAILS + .setUserAuthorizationUri("Not a URI " + "because there is no client"); + DEFAULT_RESOURCE_DETAILS + .setAccessTokenUri("Not a URI " + "because there is no client"); + } + + private final List customizers; + + private final OAuth2ProtectedResourceDetails details; + + private final OAuth2ClientContext oauth2ClientContext; + + private OAuth2RestTemplate template; + + public UserInfoRestTemplateConfiguration( + ObjectProvider> customizersProvider, + ObjectProvider detailsProvider, + ObjectProvider oauth2ClientContextProvider) { + this.customizers = customizersProvider.getIfAvailable(); + this.details = detailsProvider.getIfAvailable(); + this.oauth2ClientContext = oauth2ClientContextProvider.getIfAvailable(); + } + + // Not a @Bean: use this method as a factory + public OAuth2RestTemplate userInfoRestTemplate() { + if (this.template == null) { + this.template = getTemplate( + this.details == null ? DEFAULT_RESOURCE_DETAILS : this.details); + this.template.getInterceptors().add(new AcceptJsonRequestInterceptor()); + AuthorizationCodeAccessTokenProvider accessTokenProvider = new AuthorizationCodeAccessTokenProvider(); + accessTokenProvider.setTokenRequestEnhancer(new AcceptJsonRequestEnhancer()); + this.template.setAccessTokenProvider(accessTokenProvider); + if (!CollectionUtils.isEmpty(this.customizers)) { + AnnotationAwareOrderComparator.sort(this.customizers); + for (UserInfoRestTemplateCustomizer customizer : this.customizers) { + customizer.customize(this.template); + } + } + } + return this.template; + } + + private OAuth2RestTemplate getTemplate(OAuth2ProtectedResourceDetails details) { + if (this.oauth2ClientContext == null) { + return new OAuth2RestTemplate(details); + } + return new OAuth2RestTemplate(details, this.oauth2ClientContext); + } + +} \ No newline at end of file diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/oauth2/OAuth2AutoConfigurationTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/oauth2/OAuth2AutoConfigurationTests.java index 3acc278b92d..e7eb64d720f 100644 --- a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/oauth2/OAuth2AutoConfigurationTests.java +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/oauth2/OAuth2AutoConfigurationTests.java @@ -65,6 +65,7 @@ import org.springframework.security.config.annotation.web.configuration.WebSecur import org.springframework.security.core.authority.AuthorityUtils; import org.springframework.security.crypto.codec.Base64; import org.springframework.security.oauth2.client.OAuth2ClientContext; +import org.springframework.security.oauth2.client.OAuth2RestOperations; import org.springframework.security.oauth2.client.token.grant.client.ClientCredentialsResourceDetails; import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer; import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter; @@ -137,6 +138,8 @@ public class OAuth2AutoConfigurationTests { assertThat(handler).isInstanceOf(ApprovalStoreUserApprovalHandler.class); assertThat(clientDetails).isEqualTo(config); verifyAuthentication(config); + assertThat(this.context.getBeanNamesForType(OAuth2RestOperations.class)) + .isEmpty(); } @Test diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/oauth2/sso/CustomRestTemplateBasicOAuth2SsoConfigurationTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/oauth2/sso/CustomRestTemplateBasicOAuth2SsoConfigurationTests.java index 667606b1437..1dee191cd49 100644 --- a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/oauth2/sso/CustomRestTemplateBasicOAuth2SsoConfigurationTests.java +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/oauth2/sso/CustomRestTemplateBasicOAuth2SsoConfigurationTests.java @@ -63,7 +63,7 @@ public class CustomRestTemplateBasicOAuth2SsoConfigurationTests { public void customRestTemplateCanBePrimary() { RestTemplate restTemplate = this.restTemplateProvider.getIfAvailable(); verifyZeroInteractions(restTemplate); - assertThat(this.applicationContext.getBeansOfType(RestTemplate.class)).hasSize(2); + assertThat(this.applicationContext.getBeansOfType(RestTemplate.class)).hasSize(1); } @Configuration