Extract user info rest template into a factory
Instead of using a @Bean, it is better to use an opaque factory for an internal dependency that users are not going to want in the context. OAuth2RestTemplate is a common enough bean type that creating on in autoconfig makes it hard for users to add their own for business use. See gh-5967
This commit is contained in:
parent
3734b666df
commit
684c8c81a3
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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("<N/A>");
|
||||
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<UserInfoRestTemplateCustomizer> customizers;
|
||||
|
||||
private final OAuth2ProtectedResourceDetails details;
|
||||
|
||||
private final OAuth2ClientContext oauth2ClientContext;
|
||||
|
||||
public UserInfoRestTemplateConfiguration(
|
||||
ObjectProvider<List<UserInfoRestTemplateCustomizer>> customizersProvider,
|
||||
ObjectProvider<OAuth2ProtectedResourceDetails> detailsProvider,
|
||||
ObjectProvider<OAuth2ClientContext> 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<OAuth2ConnectionFactory<?>> connectionFactoryProvider,
|
||||
@Qualifier("userInfoRestTemplate") ObjectProvider<OAuth2RestOperations> restTemplateProvider,
|
||||
UserInfoRestTemplateConfiguration restTemplateProvider,
|
||||
ObjectProvider<AuthoritiesExtractor> 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<OAuth2RestOperations> restTemplateProvider,
|
||||
UserInfoRestTemplateConfiguration restTemplateProvider,
|
||||
ObjectProvider<AuthoritiesExtractor> 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,
|
||||
|
|
|
|||
|
|
@ -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("<N/A>");
|
||||
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<UserInfoRestTemplateCustomizer> customizers;
|
||||
|
||||
private final OAuth2ProtectedResourceDetails details;
|
||||
|
||||
private final OAuth2ClientContext oauth2ClientContext;
|
||||
|
||||
private OAuth2RestTemplate template;
|
||||
|
||||
public UserInfoRestTemplateConfiguration(
|
||||
ObjectProvider<List<UserInfoRestTemplateCustomizer>> customizersProvider,
|
||||
ObjectProvider<OAuth2ProtectedResourceDetails> detailsProvider,
|
||||
ObjectProvider<OAuth2ClientContext> 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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in New Issue