Skip to content

Commit a8837eb

Browse files
committed
Fix to allow multiple PasswordEncoder beans
Closes gh-1610
1 parent 14e5a8c commit a8837eb

File tree

2 files changed

+40
-18
lines changed

2 files changed

+40
-18
lines changed

oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OAuth2ConfigurerUtils.java

Lines changed: 2 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,9 @@
1515
*/
1616
package org.springframework.security.oauth2.server.authorization.config.annotation.web.configurers;
1717

18-
import java.util.Map;
19-
2018
import com.nimbusds.jose.jwk.source.JWKSource;
2119
import com.nimbusds.jose.proc.SecurityContext;
2220

23-
import org.springframework.beans.factory.BeanFactoryUtils;
2421
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
2522
import org.springframework.beans.factory.NoUniqueBeanDefinitionException;
2623
import org.springframework.context.ApplicationContext;
@@ -44,7 +41,6 @@
4441
import org.springframework.security.oauth2.server.authorization.token.OAuth2TokenCustomizer;
4542
import org.springframework.security.oauth2.server.authorization.token.OAuth2TokenGenerator;
4643
import org.springframework.util.Assert;
47-
import org.springframework.util.StringUtils;
4844

4945
/**
5046
* Utility methods for the OAuth 2.0 Configurers.
@@ -223,24 +219,12 @@ static <T> T getBean(HttpSecurity httpSecurity, ResolvableType type) {
223219
}
224220

225221
static <T> T getOptionalBean(HttpSecurity httpSecurity, Class<T> type) {
226-
Map<String, T> beansMap = BeanFactoryUtils
227-
.beansOfTypeIncludingAncestors(httpSecurity.getSharedObject(ApplicationContext.class), type);
228-
if (beansMap.size() > 1) {
229-
throw new NoUniqueBeanDefinitionException(type, beansMap.size(),
230-
"Expected single matching bean of type '" + type.getName() + "' but found " + beansMap.size() + ": "
231-
+ StringUtils.collectionToCommaDelimitedString(beansMap.keySet()));
232-
}
233-
return (!beansMap.isEmpty() ? beansMap.values().iterator().next() : null);
222+
return httpSecurity.getSharedObject(ApplicationContext.class).getBeanProvider(type).getIfUnique();
234223
}
235224

236225
@SuppressWarnings("unchecked")
237226
static <T> T getOptionalBean(HttpSecurity httpSecurity, ResolvableType type) {
238-
ApplicationContext context = httpSecurity.getSharedObject(ApplicationContext.class);
239-
String[] names = context.getBeanNamesForType(type);
240-
if (names.length > 1) {
241-
throw new NoUniqueBeanDefinitionException(type, names);
242-
}
243-
return (names.length == 1) ? (T) context.getBean(names[0]) : null;
227+
return (T) httpSecurity.getSharedObject(ApplicationContext.class).getBeanProvider(type).getIfUnique();
244228
}
245229

246230
}

oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OAuth2ClientCredentialsGrantTests.java

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
import org.springframework.context.annotation.Bean;
4545
import org.springframework.context.annotation.Configuration;
4646
import org.springframework.context.annotation.Import;
47+
import org.springframework.context.annotation.Primary;
4748
import org.springframework.http.HttpHeaders;
4849
import org.springframework.jdbc.core.JdbcOperations;
4950
import org.springframework.jdbc.core.JdbcTemplate;
@@ -159,6 +160,8 @@ public class OAuth2ClientCredentialsGrantTests {
159160

160161
private static AuthenticationFailureHandler authenticationFailureHandler;
161162

163+
private static PasswordEncoder passwordEncoder;
164+
162165
public final SpringTestContext spring = new SpringTestContext();
163166

164167
@Autowired
@@ -184,6 +187,9 @@ public static void init() {
184187
authenticationProvidersConsumer = mock(Consumer.class);
185188
authenticationSuccessHandler = mock(AuthenticationSuccessHandler.class);
186189
authenticationFailureHandler = mock(AuthenticationFailureHandler.class);
190+
passwordEncoder = mock(PasswordEncoder.class);
191+
given(passwordEncoder.matches(any(), any())).willReturn(true);
192+
given(passwordEncoder.upgradeEncoding(any())).willReturn(false);
187193
db = new EmbeddedDatabaseBuilder().generateUniqueName(true)
188194
.setType(EmbeddedDatabaseType.HSQL)
189195
.setScriptEncoding("UTF-8")
@@ -495,6 +501,26 @@ public void requestWhenTokenRequestWithDPoPProofThenReturnDPoPBoundAccessToken()
495501
.andExpect(jsonPath("$.token_type").value(OAuth2AccessToken.TokenType.DPOP.getValue()));
496502
}
497503

504+
@Test
505+
public void requestWhenTokenRequestWithMultiplePasswordEncodersThenPrimaryPasswordEncoderUsed() throws Exception {
506+
this.spring.register(AuthorizationServerConfigurationWithMultiplePasswordEncoders.class).autowire();
507+
508+
RegisteredClient registeredClient = TestRegisteredClients.registeredClient2().build();
509+
this.registeredClientRepository.save(registeredClient);
510+
511+
this.mvc
512+
.perform(post(DEFAULT_TOKEN_ENDPOINT_URI)
513+
.param(OAuth2ParameterNames.GRANT_TYPE, AuthorizationGrantType.CLIENT_CREDENTIALS.getValue())
514+
.param(OAuth2ParameterNames.SCOPE, "scope1 scope2")
515+
.header(HttpHeaders.AUTHORIZATION,
516+
"Basic " + encodeBasicAuth(registeredClient.getClientId(), registeredClient.getClientSecret())))
517+
.andExpect(status().isOk())
518+
.andExpect(jsonPath("$.access_token").isNotEmpty())
519+
.andExpect(jsonPath("$.scope").value("scope1 scope2"));
520+
521+
verify(passwordEncoder).matches(any(), any());
522+
}
523+
498524
private static String generateDPoPProof(String tokenEndpointUri) {
499525
// @formatter:off
500526
Map<String, Object> publicJwk = TestJwks.DEFAULT_EC_JWK
@@ -685,4 +711,16 @@ AuthorizationServerSettings authorizationServerSettings() {
685711

686712
}
687713

714+
@EnableWebSecurity
715+
@Configuration(proxyBeanMethods = false)
716+
static class AuthorizationServerConfigurationWithMultiplePasswordEncoders extends AuthorizationServerConfiguration {
717+
718+
@Primary
719+
@Bean
720+
PasswordEncoder primaryPasswordEncoder() {
721+
return passwordEncoder;
722+
}
723+
724+
}
725+
688726
}

0 commit comments

Comments
 (0)