diff --git a/config/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/server/resource/OAuth2ResourceServerConfigurer.java b/config/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/server/resource/OAuth2ResourceServerConfigurer.java index 2131074bee..e603a9cae5 100644 --- a/config/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/server/resource/OAuth2ResourceServerConfigurer.java +++ b/config/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/server/resource/OAuth2ResourceServerConfigurer.java @@ -256,8 +256,11 @@ public void init(H http) { @Override public void configure(H http) { + // Use the authenticationManagerResolver if configured, as it takes precedence + // over any jwt() or opaqueToken() configuration AuthenticationManagerResolver resolver = this.authenticationManagerResolver; if (resolver == null) { + // Fall back to jwt() or opaqueToken() configuration AuthenticationManager authenticationManager = getAuthenticationManager(http); resolver = (request) -> authenticationManager; } @@ -282,11 +285,9 @@ private void validateConfiguration() { Assert.state(this.jwtConfigurer == null || this.opaqueTokenConfigurer == null, "Spring Security only supports JWTs or Opaque Tokens, not both at the " + "same time."); } - else { - Assert.state(this.jwtConfigurer == null && this.opaqueTokenConfigurer == null, - "If an authenticationManagerResolver() is configured, then it takes " - + "precedence over any jwt() or opaqueToken() configuration."); - } + // When authenticationManagerResolver is configured, it takes precedence over + // jwt() or opaqueToken() + // configuration, so no validation is needed for that case } private void registerDefaultAccessDeniedHandler(H http) { diff --git a/config/src/test/java/org/springframework/security/config/annotation/web/configurers/oauth2/server/resource/OAuth2ResourceServerConfigurerTests.java b/config/src/test/java/org/springframework/security/config/annotation/web/configurers/oauth2/server/resource/OAuth2ResourceServerConfigurerTests.java index d9915d0152..b44e2b2c00 100644 --- a/config/src/test/java/org/springframework/security/config/annotation/web/configurers/oauth2/server/resource/OAuth2ResourceServerConfigurerTests.java +++ b/config/src/test/java/org/springframework/security/config/annotation/web/configurers/oauth2/server/resource/OAuth2ResourceServerConfigurerTests.java @@ -1339,10 +1339,18 @@ public void configureWhenUsingBothJwtAndOpaqueThenWiringException() { } @Test - public void configureWhenUsingBothAuthenticationManagerResolverAndOpaqueThenWiringException() { - assertThatExceptionOfType(BeanCreationException.class) - .isThrownBy(() -> this.spring.register(AuthenticationManagerResolverPlusOtherConfig.class).autowire()) - .withMessageContaining("authenticationManagerResolver"); + public void configureWhenUsingBothAuthenticationManagerResolverAndOpaqueThenAuthenticationManagerResolverTakesPrecedence() { + // authenticationManagerResolver should take precedence over opaqueToken + // configuration + this.spring.register(AuthenticationManagerResolverPlusOtherConfig.class).autowire(); + // No exception should be thrown + } + + @Test + public void configureWhenUsingBothAuthenticationManagerResolverAndJwtThenAuthenticationManagerResolverTakesPrecedence() { + // authenticationManagerResolver should take precedence over jwt configuration + this.spring.register(AuthenticationManagerResolverPlusJwtConfig.class).autowire(); + // No exception should be thrown } @Test @@ -2601,6 +2609,11 @@ SecurityFilterChain filterChain(HttpSecurity http) throws Exception { @EnableWebSecurity static class AuthenticationManagerResolverPlusOtherConfig { + @Bean + OpaqueTokenIntrospector opaqueTokenIntrospector() { + return mock(OpaqueTokenIntrospector.class); + } + @Bean SecurityFilterChain filterChain(HttpSecurity http) throws Exception { // @formatter:off @@ -2608,8 +2621,8 @@ SecurityFilterChain filterChain(HttpSecurity http) throws Exception { .authorizeHttpRequests((requests) -> requests .anyRequest().authenticated()) .oauth2ResourceServer((server) -> server - .authenticationManagerResolver(mock(AuthenticationManagerResolver.class)) - .opaqueToken(Customizer.withDefaults())); + .opaqueToken(Customizer.withDefaults()) + .authenticationManagerResolver(mock(AuthenticationManagerResolver.class))); return http.build(); // @formatter:on } @@ -2788,4 +2801,28 @@ public MockHttpServletRequest postProcessRequest(MockHttpServletRequest request) } + @Configuration + @EnableWebSecurity + static class AuthenticationManagerResolverPlusJwtConfig { + + @Bean + JwtDecoder jwtDecoder() { + return mock(JwtDecoder.class); + } + + @Bean + SecurityFilterChain filterChain(HttpSecurity http) throws Exception { + // @formatter:off + http + .authorizeHttpRequests((requests) -> requests + .anyRequest().authenticated()) + .oauth2ResourceServer((server) -> server + .jwt(Customizer.withDefaults()) + .authenticationManagerResolver(mock(AuthenticationManagerResolver.class))); + return http.build(); + // @formatter:on + } + + } + }