11/*
2- * Copyright 2002-2022 the original author or authors.
2+ * Copyright 2002-2023 the original author or authors.
33 *
44 * Licensed under the Apache License, Version 2.0 (the "License");
55 * you may not use this file except in compliance with the License.
1818
1919import java .util .List ;
2020
21+ import org .springframework .beans .BeanMetadataElement ;
22+ import org .springframework .beans .BeansException ;
23+ import org .springframework .beans .factory .BeanFactory ;
24+ import org .springframework .beans .factory .BeanFactoryAware ;
25+ import org .springframework .beans .factory .BeanFactoryUtils ;
26+ import org .springframework .beans .factory .ListableBeanFactory ;
2127import org .springframework .beans .factory .annotation .Autowired ;
28+ import org .springframework .beans .factory .config .BeanDefinition ;
29+ import org .springframework .beans .factory .config .ConfigurableListableBeanFactory ;
30+ import org .springframework .beans .factory .config .RuntimeBeanReference ;
31+ import org .springframework .beans .factory .support .BeanDefinitionBuilder ;
32+ import org .springframework .beans .factory .support .BeanDefinitionRegistry ;
33+ import org .springframework .beans .factory .support .BeanDefinitionRegistryPostProcessor ;
34+ import org .springframework .beans .factory .support .ManagedList ;
35+ import org .springframework .context .annotation .AnnotationBeanNameGenerator ;
36+ import org .springframework .context .annotation .Bean ;
2237import org .springframework .context .annotation .Configuration ;
2338import org .springframework .context .annotation .Import ;
2439import org .springframework .context .annotation .ImportSelector ;
40+ import org .springframework .core .ResolvableType ;
2541import org .springframework .core .type .AnnotationMetadata ;
2642import org .springframework .security .core .context .SecurityContextHolderStrategy ;
43+ import org .springframework .security .oauth2 .client .AuthorizationCodeOAuth2AuthorizedClientProvider ;
44+ import org .springframework .security .oauth2 .client .ClientCredentialsOAuth2AuthorizedClientProvider ;
45+ import org .springframework .security .oauth2 .client .DelegatingOAuth2AuthorizedClientProvider ;
2746import org .springframework .security .oauth2 .client .OAuth2AuthorizedClientManager ;
2847import org .springframework .security .oauth2 .client .OAuth2AuthorizedClientProvider ;
2948import org .springframework .security .oauth2 .client .OAuth2AuthorizedClientProviderBuilder ;
49+ import org .springframework .security .oauth2 .client .PasswordOAuth2AuthorizedClientProvider ;
50+ import org .springframework .security .oauth2 .client .RefreshTokenOAuth2AuthorizedClientProvider ;
3051import org .springframework .security .oauth2 .client .endpoint .OAuth2AccessTokenResponseClient ;
3152import org .springframework .security .oauth2 .client .endpoint .OAuth2ClientCredentialsGrantRequest ;
53+ import org .springframework .security .oauth2 .client .endpoint .OAuth2PasswordGrantRequest ;
54+ import org .springframework .security .oauth2 .client .endpoint .OAuth2RefreshTokenGrantRequest ;
3255import org .springframework .security .oauth2 .client .registration .ClientRegistrationRepository ;
3356import org .springframework .security .oauth2 .client .web .DefaultOAuth2AuthorizedClientManager ;
3457import org .springframework .security .oauth2 .client .web .OAuth2AuthorizedClientRepository ;
4871 * @since 5.1
4972 * @see OAuth2ImportSelector
5073 */
51- @ Import (OAuth2ClientConfiguration .OAuth2ClientWebMvcImportSelector .class )
74+ @ Import ({ OAuth2ClientConfiguration .OAuth2ClientWebMvcImportSelector .class ,
75+ OAuth2ClientConfiguration .OAuth2AuthorizedClientManagerConfiguration .class })
5276final class OAuth2ClientConfiguration {
5377
5478 private static final boolean webMvcPresent ;
@@ -65,8 +89,22 @@ public String[] selectImports(AnnotationMetadata importingClassMetadata) {
6589 if (!webMvcPresent ) {
6690 return new String [0 ];
6791 }
68- return new String [] { "org.springframework.security.config.annotation.web.configuration."
69- + "OAuth2ClientConfiguration.OAuth2ClientWebMvcSecurityConfiguration" };
92+ return new String [] {
93+ OAuth2ClientConfiguration .class .getName () + ".OAuth2ClientWebMvcSecurityConfiguration" };
94+ }
95+
96+ }
97+
98+ /**
99+ * @author Joe Grandja
100+ * @since 6.2.0
101+ */
102+ @ Configuration (proxyBeanMethods = false )
103+ static class OAuth2AuthorizedClientManagerConfiguration {
104+
105+ @ Bean
106+ OAuth2AuthorizedClientManagerRegistrar authorizedClientManagerRegistrar () {
107+ return new OAuth2AuthorizedClientManagerRegistrar ();
70108 }
71109
72110 }
@@ -160,4 +198,136 @@ private OAuth2AuthorizedClientManager getAuthorizedClientManager() {
160198
161199 }
162200
201+ /**
202+ * A registrar for registering the default {@link OAuth2AuthorizedClientManager} bean
203+ * definition, if not already present.
204+ *
205+ * @author Joe Grandja
206+ * @since 6.2.0
207+ */
208+ static class OAuth2AuthorizedClientManagerRegistrar
209+ implements BeanDefinitionRegistryPostProcessor , BeanFactoryAware {
210+
211+ private final AnnotationBeanNameGenerator beanNameGenerator = new AnnotationBeanNameGenerator ();
212+
213+ private BeanFactory beanFactory ;
214+
215+ @ Override
216+ public void postProcessBeanDefinitionRegistry (BeanDefinitionRegistry registry ) throws BeansException {
217+ String [] authorizedClientManagerBeanNames = BeanFactoryUtils .beanNamesForTypeIncludingAncestors (
218+ (ListableBeanFactory ) this .beanFactory , OAuth2AuthorizedClientManager .class , true , true );
219+ if (authorizedClientManagerBeanNames .length != 0 ) {
220+ return ;
221+ }
222+
223+ String [] clientRegistrationRepositoryBeanNames = BeanFactoryUtils .beanNamesForTypeIncludingAncestors (
224+ (ListableBeanFactory ) this .beanFactory , ClientRegistrationRepository .class , true , true );
225+ String [] authorizedClientRepositoryBeanNames = BeanFactoryUtils .beanNamesForTypeIncludingAncestors (
226+ (ListableBeanFactory ) this .beanFactory , OAuth2AuthorizedClientRepository .class , true , true );
227+ if (clientRegistrationRepositoryBeanNames .length != 1 || authorizedClientRepositoryBeanNames .length != 1 ) {
228+ return ;
229+ }
230+
231+ BeanDefinition beanDefinition = BeanDefinitionBuilder
232+ .genericBeanDefinition (DefaultOAuth2AuthorizedClientManager .class )
233+ .addConstructorArgReference (clientRegistrationRepositoryBeanNames [0 ])
234+ .addConstructorArgReference (authorizedClientRepositoryBeanNames [0 ])
235+ .addPropertyValue ("authorizedClientProvider" , getAuthorizedClientProvider ()).getBeanDefinition ();
236+
237+ registry .registerBeanDefinition (this .beanNameGenerator .generateBeanName (beanDefinition , registry ),
238+ beanDefinition );
239+ }
240+
241+ @ Override
242+ public void postProcessBeanFactory (ConfigurableListableBeanFactory beanFactory ) throws BeansException {
243+ }
244+
245+ private BeanDefinition getAuthorizedClientProvider () {
246+ ManagedList <Object > authorizedClientProviders = new ManagedList <>();
247+ authorizedClientProviders .add (getAuthorizationCodeAuthorizedClientProvider ());
248+ authorizedClientProviders .add (getRefreshTokenAuthorizedClientProvider ());
249+ authorizedClientProviders .add (getClientCredentialsAuthorizedClientProvider ());
250+ authorizedClientProviders .add (getPasswordAuthorizedClientProvider ());
251+ return BeanDefinitionBuilder .genericBeanDefinition (DelegatingOAuth2AuthorizedClientProvider .class )
252+ .addConstructorArgValue (authorizedClientProviders ).getBeanDefinition ();
253+ }
254+
255+ private BeanMetadataElement getAuthorizationCodeAuthorizedClientProvider () {
256+ String [] beanNames = BeanFactoryUtils .beanNamesForTypeIncludingAncestors (
257+ (ListableBeanFactory ) this .beanFactory , AuthorizationCodeOAuth2AuthorizedClientProvider .class , true ,
258+ true );
259+ if (beanNames .length == 1 ) {
260+ return new RuntimeBeanReference (beanNames [0 ]);
261+ }
262+
263+ return BeanDefinitionBuilder .genericBeanDefinition (AuthorizationCodeOAuth2AuthorizedClientProvider .class )
264+ .getBeanDefinition ();
265+ }
266+
267+ private BeanMetadataElement getRefreshTokenAuthorizedClientProvider () {
268+ String [] beanNames = BeanFactoryUtils .beanNamesForTypeIncludingAncestors (
269+ (ListableBeanFactory ) this .beanFactory , RefreshTokenOAuth2AuthorizedClientProvider .class , true ,
270+ true );
271+ if (beanNames .length == 1 ) {
272+ return new RuntimeBeanReference (beanNames [0 ]);
273+ }
274+
275+ BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder
276+ .genericBeanDefinition (RefreshTokenOAuth2AuthorizedClientProvider .class );
277+ ResolvableType resolvableType = ResolvableType .forClassWithGenerics (OAuth2AccessTokenResponseClient .class ,
278+ OAuth2RefreshTokenGrantRequest .class );
279+ beanNames = BeanFactoryUtils .beanNamesForTypeIncludingAncestors ((ListableBeanFactory ) this .beanFactory ,
280+ resolvableType , true , true );
281+ if (beanNames .length == 1 ) {
282+ beanDefinitionBuilder .addPropertyReference ("accessTokenResponseClient" , beanNames [0 ]);
283+ }
284+ return beanDefinitionBuilder .getBeanDefinition ();
285+ }
286+
287+ private BeanMetadataElement getClientCredentialsAuthorizedClientProvider () {
288+ String [] beanNames = BeanFactoryUtils .beanNamesForTypeIncludingAncestors (
289+ (ListableBeanFactory ) this .beanFactory , ClientCredentialsOAuth2AuthorizedClientProvider .class , true ,
290+ true );
291+ if (beanNames .length == 1 ) {
292+ return new RuntimeBeanReference (beanNames [0 ]);
293+ }
294+
295+ BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder
296+ .genericBeanDefinition (ClientCredentialsOAuth2AuthorizedClientProvider .class );
297+ ResolvableType resolvableType = ResolvableType .forClassWithGenerics (OAuth2AccessTokenResponseClient .class ,
298+ OAuth2ClientCredentialsGrantRequest .class );
299+ beanNames = BeanFactoryUtils .beanNamesForTypeIncludingAncestors ((ListableBeanFactory ) this .beanFactory ,
300+ resolvableType , true , true );
301+ if (beanNames .length == 1 ) {
302+ beanDefinitionBuilder .addPropertyReference ("accessTokenResponseClient" , beanNames [0 ]);
303+ }
304+ return beanDefinitionBuilder .getBeanDefinition ();
305+ }
306+
307+ private BeanMetadataElement getPasswordAuthorizedClientProvider () {
308+ String [] beanNames = BeanFactoryUtils .beanNamesForTypeIncludingAncestors (
309+ (ListableBeanFactory ) this .beanFactory , PasswordOAuth2AuthorizedClientProvider .class , true , true );
310+ if (beanNames .length == 1 ) {
311+ return new RuntimeBeanReference (beanNames [0 ]);
312+ }
313+
314+ BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder
315+ .genericBeanDefinition (PasswordOAuth2AuthorizedClientProvider .class );
316+ ResolvableType resolvableType = ResolvableType .forClassWithGenerics (OAuth2AccessTokenResponseClient .class ,
317+ OAuth2PasswordGrantRequest .class );
318+ beanNames = BeanFactoryUtils .beanNamesForTypeIncludingAncestors ((ListableBeanFactory ) this .beanFactory ,
319+ resolvableType , true , true );
320+ if (beanNames .length == 1 ) {
321+ beanDefinitionBuilder .addPropertyReference ("accessTokenResponseClient" , beanNames [0 ]);
322+ }
323+ return beanDefinitionBuilder .getBeanDefinition ();
324+ }
325+
326+ @ Override
327+ public void setBeanFactory (BeanFactory beanFactory ) throws BeansException {
328+ this .beanFactory = beanFactory ;
329+ }
330+
331+ }
332+
163333}
0 commit comments