diff --git a/pom.xml b/pom.xml
index 0bcf654b3..c05e566c5 100644
--- a/pom.xml
+++ b/pom.xml
@@ -80,6 +80,7 @@
1.7.1
3.3.0
3.0.1
+
2.2.0.RELEASE
diff --git a/spring-cloud-sleuth-autoconfigure/pom.xml b/spring-cloud-sleuth-autoconfigure/pom.xml
index dede89845..d0646bbba 100644
--- a/spring-cloud-sleuth-autoconfigure/pom.xml
+++ b/spring-cloud-sleuth-autoconfigure/pom.xml
@@ -153,6 +153,12 @@
spring-security-oauth2-autoconfigure
true
+
+ org.springframework.security
+ spring-security-oauth2-client
+ true
+
+
diff --git a/spring-cloud-sleuth-autoconfigure/src/main/java/org/springframework/cloud/sleuth/autoconfig/instrument/web/client/TraceWebClientAutoConfiguration.java b/spring-cloud-sleuth-autoconfigure/src/main/java/org/springframework/cloud/sleuth/autoconfig/instrument/web/client/TraceWebClientAutoConfiguration.java
index 3e7d85c46..17e740773 100644
--- a/spring-cloud-sleuth-autoconfigure/src/main/java/org/springframework/cloud/sleuth/autoconfig/instrument/web/client/TraceWebClientAutoConfiguration.java
+++ b/spring-cloud-sleuth-autoconfigure/src/main/java/org/springframework/cloud/sleuth/autoconfig/instrument/web/client/TraceWebClientAutoConfiguration.java
@@ -18,9 +18,11 @@ package org.springframework.cloud.sleuth.autoconfig.instrument.web.client;
import reactor.netty.http.client.HttpClient;
+import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
@@ -38,6 +40,7 @@ import org.springframework.cloud.sleuth.autoconfig.brave.BraveAutoConfiguration;
import org.springframework.cloud.sleuth.http.HttpClientHandler;
import org.springframework.cloud.sleuth.instrument.web.client.HttpClientBeanPostProcessor;
import org.springframework.cloud.sleuth.instrument.web.client.LazyTraceClientHttpRequestInterceptor;
+import org.springframework.cloud.sleuth.instrument.web.client.TraceDefaultOAuth2UserServiceCustomizer;
import org.springframework.cloud.sleuth.instrument.web.client.TraceRequestHttpHeadersFilter;
import org.springframework.cloud.sleuth.instrument.web.client.TraceResponseHttpHeadersFilter;
import org.springframework.cloud.sleuth.instrument.web.client.TraceRestTemplateBeanPostProcessor;
@@ -52,6 +55,7 @@ import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.security.oauth2.client.OAuth2RestTemplate;
+import org.springframework.security.oauth2.client.userinfo.DefaultOAuth2UserService;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.reactive.function.client.WebClient;
@@ -148,6 +152,7 @@ class TraceWebClientAutoConfiguration {
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({ UserInfoRestTemplateCustomizer.class, OAuth2RestTemplate.class })
+ @Deprecated // Use Spring-Security OAuth2 support
protected static class TraceOAuthConfiguration {
@Bean
@@ -164,4 +169,24 @@ class TraceWebClientAutoConfiguration {
}
+ @Configuration(proxyBeanMethods = false)
+ @ConditionalOnClass(DefaultOAuth2UserService.class)
+ protected static class TraceSpringSecurityOAuth2Configuration {
+
+ @Bean
+ static BeanPostProcessor traceDefaultOAuth2UserServiceBeanPostProcessor(BeanFactory beanFactory) {
+ return new BeanPostProcessor() {
+ @Override
+ public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
+ if (bean instanceof DefaultOAuth2UserService) {
+ new TraceDefaultOAuth2UserServiceCustomizer(beanFactory)
+ .customize((DefaultOAuth2UserService) bean);
+ }
+ return bean;
+ }
+ };
+ }
+
+ }
+
}
diff --git a/spring-cloud-sleuth-instrumentation/pom.xml b/spring-cloud-sleuth-instrumentation/pom.xml
index 9ef5c2232..789dc8c0a 100644
--- a/spring-cloud-sleuth-instrumentation/pom.xml
+++ b/spring-cloud-sleuth-instrumentation/pom.xml
@@ -152,6 +152,11 @@
spring-security-oauth2-autoconfigure
true
+
+ org.springframework.security
+ spring-security-oauth2-client
+ true
+
org.springframework.boot
diff --git a/spring-cloud-sleuth-instrumentation/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/TraceDefaultOAuth2UserServiceCustomizer.java b/spring-cloud-sleuth-instrumentation/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/TraceDefaultOAuth2UserServiceCustomizer.java
new file mode 100644
index 000000000..336dbbd06
--- /dev/null
+++ b/spring-cloud-sleuth-instrumentation/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/TraceDefaultOAuth2UserServiceCustomizer.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2013-2021 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 org.springframework.cloud.sleuth.instrument.web.client;
+
+import java.lang.reflect.Field;
+import java.util.Objects;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.springframework.beans.factory.BeanFactory;
+import org.springframework.cloud.sleuth.instrument.web.mvc.TracingClientHttpRequestInterceptor;
+import org.springframework.security.oauth2.client.userinfo.DefaultOAuth2UserService;
+import org.springframework.util.ReflectionUtils;
+import org.springframework.web.client.RestOperations;
+import org.springframework.web.client.RestTemplate;
+
+/**
+ * Customizes a {@link DefaultOAuth2UserService} by providing it with a trace interceptor.
+ *
+ * @author Marcin Grzejszczak
+ * @since 3.0.6
+ */
+public class TraceDefaultOAuth2UserServiceCustomizer {
+
+ private static final Log log = LogFactory.getLog(TraceDefaultOAuth2UserServiceCustomizer.class);
+
+ private final BeanFactory beanFactory;
+
+ private static final Field REST_OPERATIONS = ReflectionUtils.findField(DefaultOAuth2UserService.class,
+ "restOperations", RestOperations.class);
+
+ public TraceDefaultOAuth2UserServiceCustomizer(BeanFactory beanFactory) {
+ this.beanFactory = beanFactory;
+ }
+
+ public void customize(DefaultOAuth2UserService service) {
+ try {
+ ReflectionUtils.makeAccessible(Objects.requireNonNull(REST_OPERATIONS,
+ "restOperations field was not found in [DefaultOAuth2UserService] class"));
+ RestOperations restOperations = (RestOperations) REST_OPERATIONS.get(service);
+ if (!(restOperations instanceof RestTemplate)) {
+ log.warn(
+ "Won't instrument the restOperations field in [DefaultOAuth2UserService] class because it's not a RestTemplate object");
+ return;
+ }
+ RestTemplate template = (RestTemplate) restOperations;
+ final TracingClientHttpRequestInterceptor interceptor = this.beanFactory
+ .getBean(TracingClientHttpRequestInterceptor.class);
+ new RestTemplateInterceptorInjector(interceptor).inject(template);
+ }
+ catch (Exception e) {
+ log.warn("Can't access the restOperations field - won't instrument the [DefaultOAuth2UserService] class",
+ e);
+ }
+ }
+
+}
diff --git a/spring-cloud-sleuth-instrumentation/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/TraceDefaultOAuth2UserServiceCustomizerTests.java b/spring-cloud-sleuth-instrumentation/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/TraceDefaultOAuth2UserServiceCustomizerTests.java
new file mode 100644
index 000000000..e5b020bdc
--- /dev/null
+++ b/spring-cloud-sleuth-instrumentation/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/TraceDefaultOAuth2UserServiceCustomizerTests.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2013-2021 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 org.springframework.cloud.sleuth.instrument.web.client;
+
+import org.assertj.core.api.BDDAssertions;
+import org.junit.jupiter.api.Test;
+
+import org.springframework.beans.factory.BeanFactory;
+import org.springframework.beans.factory.support.StaticListableBeanFactory;
+import org.springframework.cloud.sleuth.instrument.web.mvc.TracingClientHttpRequestInterceptor;
+import org.springframework.security.oauth2.client.userinfo.DefaultOAuth2UserService;
+import org.springframework.util.ReflectionUtils;
+import org.springframework.web.client.RestTemplate;
+
+class TraceDefaultOAuth2UserServiceCustomizerTests {
+
+ @Test
+ void should_add_a_trace_interceptor_to_defaultoauth2userservice() {
+ DefaultOAuth2UserService service = new DefaultOAuth2UserService();
+ BeanFactory beanFactory = beanFactory();
+ TraceDefaultOAuth2UserServiceCustomizer serviceCustomizer = new TraceDefaultOAuth2UserServiceCustomizer(
+ beanFactory);
+
+ serviceCustomizer.customize(service);
+ serviceCustomizer.customize(service);
+
+ Object operations = ReflectionUtils
+ .getField(ReflectionUtils.findField(DefaultOAuth2UserService.class, "restOperations"), service);
+ BDDAssertions.then(operations).isInstanceOf(RestTemplate.class);
+ RestTemplate restTemplate = (RestTemplate) operations;
+ BDDAssertions.then(restTemplate.getInterceptors()).hasSize(1)
+ .hasOnlyElementsOfType(TracingClientHttpRequestInterceptor.class);
+ }
+
+ private BeanFactory beanFactory() {
+ StaticListableBeanFactory beanFactory = new StaticListableBeanFactory();
+ beanFactory.addBean("TracingClientHttpRequestInterceptor",
+ TracingClientHttpRequestInterceptor.create(null, null));
+ return beanFactory;
+ }
+
+}