From 7c97d8ede97410370bd6bf22fe3f9bf48dcd7f1b Mon Sep 17 00:00:00 2001 From: Joe Grandja Date: Fri, 18 Jun 2021 11:06:52 -0400 Subject: [PATCH] Extract utility methods from OAuth2AuthorizationServerConfigurer Issue gh-319 --- .../OAuth2AuthorizationServerConfigurer.java | 166 ++--------------- .../authorization/OAuth2ConfigurerUtils.java | 172 ++++++++++++++++++ 2 files changed, 191 insertions(+), 147 deletions(-) create mode 100644 oauth2-authorization-server/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/server/authorization/OAuth2ConfigurerUtils.java diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/server/authorization/OAuth2AuthorizationServerConfigurer.java b/oauth2-authorization-server/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/server/authorization/OAuth2AuthorizationServerConfigurer.java index d70aae08..2c52b248 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/server/authorization/OAuth2AuthorizationServerConfigurer.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/server/authorization/OAuth2AuthorizationServerConfigurer.java @@ -16,16 +16,10 @@ package org.springframework.security.config.annotation.web.configurers.oauth2.server.authorization; import java.net.URI; -import java.util.Map; import com.nimbusds.jose.jwk.source.JWKSource; import com.nimbusds.jose.proc.SecurityContext; -import org.springframework.beans.factory.BeanFactoryUtils; -import org.springframework.beans.factory.NoSuchBeanDefinitionException; -import org.springframework.beans.factory.NoUniqueBeanDefinitionException; -import org.springframework.context.ApplicationContext; -import org.springframework.core.ResolvableType; import org.springframework.http.HttpMethod; import org.springframework.http.HttpStatus; import org.springframework.security.authentication.AuthenticationManager; @@ -34,9 +28,6 @@ import org.springframework.security.config.annotation.web.configurers.AbstractHt import org.springframework.security.config.annotation.web.configurers.ExceptionHandlingConfigurer; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.oauth2.jwt.JwtEncoder; -import org.springframework.security.oauth2.jwt.NimbusJwsEncoder; -import org.springframework.security.oauth2.server.authorization.InMemoryOAuth2AuthorizationConsentService; -import org.springframework.security.oauth2.server.authorization.InMemoryOAuth2AuthorizationService; import org.springframework.security.oauth2.server.authorization.JwtEncodingContext; import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationConsentService; import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService; @@ -207,26 +198,26 @@ public final class OAuth2AuthorizationServerConfigurer jwtCustomizer = getJwtCustomizer(builder); + JwtEncoder jwtEncoder = OAuth2ConfigurerUtils.getJwtEncoder(builder); + OAuth2TokenCustomizer jwtCustomizer = OAuth2ConfigurerUtils.getJwtCustomizer(builder); OAuth2AuthorizationCodeAuthenticationProvider authorizationCodeAuthenticationProvider = new OAuth2AuthorizationCodeAuthenticationProvider( - getAuthorizationService(builder), + OAuth2ConfigurerUtils.getAuthorizationService(builder), jwtEncoder); if (jwtCustomizer != null) { authorizationCodeAuthenticationProvider.setJwtCustomizer(jwtCustomizer); @@ -235,7 +226,7 @@ public final class OAuth2AuthorizationServerConfigurer exceptionHandling = builder.getConfigurer(ExceptionHandlingConfigurer.class); @@ -283,7 +274,7 @@ public final class OAuth2AuthorizationServerConfigurer jwkSource = getJwkSource(builder); + JWKSource jwkSource = OAuth2ConfigurerUtils.getJwkSource(builder); NimbusJwkSetEndpointFilter jwkSetEndpointFilter = new NimbusJwkSetEndpointFilter( jwkSource, providerSettings.jwkSetEndpoint()); @@ -313,9 +304,9 @@ public final class OAuth2AuthorizationServerConfigurer> RegisteredClientRepository getRegisteredClientRepository(B builder) { - RegisteredClientRepository registeredClientRepository = builder.getSharedObject(RegisteredClientRepository.class); - if (registeredClientRepository == null) { - registeredClientRepository = getBean(builder, RegisteredClientRepository.class); - builder.setSharedObject(RegisteredClientRepository.class, registeredClientRepository); - } - return registeredClientRepository; - } - - private static > OAuth2AuthorizationService getAuthorizationService(B builder) { - OAuth2AuthorizationService authorizationService = builder.getSharedObject(OAuth2AuthorizationService.class); - if (authorizationService == null) { - authorizationService = getOptionalBean(builder, OAuth2AuthorizationService.class); - if (authorizationService == null) { - authorizationService = new InMemoryOAuth2AuthorizationService(); - } - builder.setSharedObject(OAuth2AuthorizationService.class, authorizationService); - } - return authorizationService; - } - - private static > OAuth2AuthorizationConsentService getAuthorizationConsentService(B builder) { - OAuth2AuthorizationConsentService authorizationConsentService = builder.getSharedObject(OAuth2AuthorizationConsentService.class); - if (authorizationConsentService == null) { - authorizationConsentService = getOptionalBean(builder, OAuth2AuthorizationConsentService.class); - if (authorizationConsentService == null) { - authorizationConsentService = new InMemoryOAuth2AuthorizationConsentService(); - } - builder.setSharedObject(OAuth2AuthorizationConsentService.class, authorizationConsentService); - } - return authorizationConsentService; - } - - private static > JwtEncoder getJwtEncoder(B builder) { - JwtEncoder jwtEncoder = builder.getSharedObject(JwtEncoder.class); - if (jwtEncoder == null) { - jwtEncoder = getOptionalBean(builder, JwtEncoder.class); - if (jwtEncoder == null) { - JWKSource jwkSource = getJwkSource(builder); - jwtEncoder = new NimbusJwsEncoder(jwkSource); - } - builder.setSharedObject(JwtEncoder.class, jwtEncoder); - } - return jwtEncoder; - } - - @SuppressWarnings("unchecked") - private static > JWKSource getJwkSource(B builder) { - JWKSource jwkSource = builder.getSharedObject(JWKSource.class); - if (jwkSource == null) { - ResolvableType type = ResolvableType.forClassWithGenerics(JWKSource.class, SecurityContext.class); - jwkSource = getBean(builder, type); - builder.setSharedObject(JWKSource.class, jwkSource); - } - return jwkSource; - } - - @SuppressWarnings("unchecked") - private static > OAuth2TokenCustomizer getJwtCustomizer(B builder) { - OAuth2TokenCustomizer jwtCustomizer = builder.getSharedObject(OAuth2TokenCustomizer.class); - if (jwtCustomizer == null) { - ResolvableType type = ResolvableType.forClassWithGenerics(OAuth2TokenCustomizer.class, JwtEncodingContext.class); - jwtCustomizer = getOptionalBean(builder, type); - if (jwtCustomizer != null) { - builder.setSharedObject(OAuth2TokenCustomizer.class, jwtCustomizer); - } - } - return jwtCustomizer; - } - - private static > ProviderSettings getProviderSettings(B builder) { - ProviderSettings providerSettings = builder.getSharedObject(ProviderSettings.class); - if (providerSettings == null) { - providerSettings = getOptionalBean(builder, ProviderSettings.class); - if (providerSettings == null) { - providerSettings = new ProviderSettings(); - } - builder.setSharedObject(ProviderSettings.class, providerSettings); - } - return providerSettings; - } - - private static , T> T getBean(B builder, Class type) { - return builder.getSharedObject(ApplicationContext.class).getBean(type); - } - - @SuppressWarnings("unchecked") - private static , T> T getBean(B builder, ResolvableType type) { - ApplicationContext context = builder.getSharedObject(ApplicationContext.class); - String[] names = context.getBeanNamesForType(type); - if (names.length == 1) { - return (T) context.getBean(names[0]); - } - if (names.length > 1) { - throw new NoUniqueBeanDefinitionException(type, names); - } - throw new NoSuchBeanDefinitionException(type); - } - - private static , T> T getOptionalBean(B builder, Class type) { - Map beansMap = BeanFactoryUtils.beansOfTypeIncludingAncestors( - builder.getSharedObject(ApplicationContext.class), type); - if (beansMap.size() > 1) { - throw new NoUniqueBeanDefinitionException(type, beansMap.size(), - "Expected single matching bean of type '" + type.getName() + "' but found " + - beansMap.size() + ": " + StringUtils.collectionToCommaDelimitedString(beansMap.keySet())); - } - return (!beansMap.isEmpty() ? beansMap.values().iterator().next() : null); - } - - @SuppressWarnings("unchecked") - private static , T> T getOptionalBean(B builder, ResolvableType type) { - ApplicationContext context = builder.getSharedObject(ApplicationContext.class); - String[] names = context.getBeanNamesForType(type); - if (names.length > 1) { - throw new NoUniqueBeanDefinitionException(type, names); - } - return names.length == 1 ? (T) context.getBean(names[0]) : null; - } } diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/server/authorization/OAuth2ConfigurerUtils.java b/oauth2-authorization-server/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/server/authorization/OAuth2ConfigurerUtils.java new file mode 100644 index 00000000..41173068 --- /dev/null +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/server/authorization/OAuth2ConfigurerUtils.java @@ -0,0 +1,172 @@ +/* + * Copyright 2020-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.security.config.annotation.web.configurers.oauth2.server.authorization; + +import java.util.Map; + +import com.nimbusds.jose.jwk.source.JWKSource; +import com.nimbusds.jose.proc.SecurityContext; + +import org.springframework.beans.factory.BeanFactoryUtils; +import org.springframework.beans.factory.NoSuchBeanDefinitionException; +import org.springframework.beans.factory.NoUniqueBeanDefinitionException; +import org.springframework.context.ApplicationContext; +import org.springframework.core.ResolvableType; +import org.springframework.security.config.annotation.web.HttpSecurityBuilder; +import org.springframework.security.oauth2.jwt.JwtEncoder; +import org.springframework.security.oauth2.jwt.NimbusJwsEncoder; +import org.springframework.security.oauth2.server.authorization.InMemoryOAuth2AuthorizationConsentService; +import org.springframework.security.oauth2.server.authorization.InMemoryOAuth2AuthorizationService; +import org.springframework.security.oauth2.server.authorization.JwtEncodingContext; +import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationConsentService; +import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService; +import org.springframework.security.oauth2.server.authorization.OAuth2TokenCustomizer; +import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository; +import org.springframework.security.oauth2.server.authorization.config.ProviderSettings; +import org.springframework.util.StringUtils; + +/** + * Utility methods for the OAuth 2.0 Configurers. + * + * @author Joe Grandja + * @since 0.1.2 + */ +final class OAuth2ConfigurerUtils { + + private OAuth2ConfigurerUtils() { + } + + static > RegisteredClientRepository getRegisteredClientRepository(B builder) { + RegisteredClientRepository registeredClientRepository = builder.getSharedObject(RegisteredClientRepository.class); + if (registeredClientRepository == null) { + registeredClientRepository = getBean(builder, RegisteredClientRepository.class); + builder.setSharedObject(RegisteredClientRepository.class, registeredClientRepository); + } + return registeredClientRepository; + } + + static > OAuth2AuthorizationService getAuthorizationService(B builder) { + OAuth2AuthorizationService authorizationService = builder.getSharedObject(OAuth2AuthorizationService.class); + if (authorizationService == null) { + authorizationService = getOptionalBean(builder, OAuth2AuthorizationService.class); + if (authorizationService == null) { + authorizationService = new InMemoryOAuth2AuthorizationService(); + } + builder.setSharedObject(OAuth2AuthorizationService.class, authorizationService); + } + return authorizationService; + } + + static > OAuth2AuthorizationConsentService getAuthorizationConsentService(B builder) { + OAuth2AuthorizationConsentService authorizationConsentService = builder.getSharedObject(OAuth2AuthorizationConsentService.class); + if (authorizationConsentService == null) { + authorizationConsentService = getOptionalBean(builder, OAuth2AuthorizationConsentService.class); + if (authorizationConsentService == null) { + authorizationConsentService = new InMemoryOAuth2AuthorizationConsentService(); + } + builder.setSharedObject(OAuth2AuthorizationConsentService.class, authorizationConsentService); + } + return authorizationConsentService; + } + + static > JwtEncoder getJwtEncoder(B builder) { + JwtEncoder jwtEncoder = builder.getSharedObject(JwtEncoder.class); + if (jwtEncoder == null) { + jwtEncoder = getOptionalBean(builder, JwtEncoder.class); + if (jwtEncoder == null) { + JWKSource jwkSource = getJwkSource(builder); + jwtEncoder = new NimbusJwsEncoder(jwkSource); + } + builder.setSharedObject(JwtEncoder.class, jwtEncoder); + } + return jwtEncoder; + } + + @SuppressWarnings("unchecked") + static > JWKSource getJwkSource(B builder) { + JWKSource jwkSource = builder.getSharedObject(JWKSource.class); + if (jwkSource == null) { + ResolvableType type = ResolvableType.forClassWithGenerics(JWKSource.class, SecurityContext.class); + jwkSource = getBean(builder, type); + builder.setSharedObject(JWKSource.class, jwkSource); + } + return jwkSource; + } + + @SuppressWarnings("unchecked") + static > OAuth2TokenCustomizer getJwtCustomizer(B builder) { + OAuth2TokenCustomizer jwtCustomizer = builder.getSharedObject(OAuth2TokenCustomizer.class); + if (jwtCustomizer == null) { + ResolvableType type = ResolvableType.forClassWithGenerics(OAuth2TokenCustomizer.class, JwtEncodingContext.class); + jwtCustomizer = getOptionalBean(builder, type); + if (jwtCustomizer != null) { + builder.setSharedObject(OAuth2TokenCustomizer.class, jwtCustomizer); + } + } + return jwtCustomizer; + } + + static > ProviderSettings getProviderSettings(B builder) { + ProviderSettings providerSettings = builder.getSharedObject(ProviderSettings.class); + if (providerSettings == null) { + providerSettings = getOptionalBean(builder, ProviderSettings.class); + if (providerSettings == null) { + providerSettings = new ProviderSettings(); + } + builder.setSharedObject(ProviderSettings.class, providerSettings); + } + return providerSettings; + } + + static , T> T getBean(B builder, Class type) { + return builder.getSharedObject(ApplicationContext.class).getBean(type); + } + + @SuppressWarnings("unchecked") + static , T> T getBean(B builder, ResolvableType type) { + ApplicationContext context = builder.getSharedObject(ApplicationContext.class); + String[] names = context.getBeanNamesForType(type); + if (names.length == 1) { + return (T) context.getBean(names[0]); + } + if (names.length > 1) { + throw new NoUniqueBeanDefinitionException(type, names); + } + throw new NoSuchBeanDefinitionException(type); + } + + static , T> T getOptionalBean(B builder, Class type) { + Map beansMap = BeanFactoryUtils.beansOfTypeIncludingAncestors( + builder.getSharedObject(ApplicationContext.class), type); + if (beansMap.size() > 1) { + throw new NoUniqueBeanDefinitionException(type, beansMap.size(), + "Expected single matching bean of type '" + type.getName() + "' but found " + + beansMap.size() + ": " + StringUtils.collectionToCommaDelimitedString(beansMap.keySet())); + } + return (!beansMap.isEmpty() ? beansMap.values().iterator().next() : null); + } + + @SuppressWarnings("unchecked") + static , T> T getOptionalBean(B builder, ResolvableType type) { + ApplicationContext context = builder.getSharedObject(ApplicationContext.class); + String[] names = context.getBeanNamesForType(type); + if (names.length > 1) { + throw new NoUniqueBeanDefinitionException(type, names); + } + return names.length == 1 ? (T) context.getBean(names[0]) : null; + } + +}