From d0d655e18df6ae1af42317e10efcf0bb93da875d Mon Sep 17 00:00:00 2001 From: Arvid Ottenberg Date: Thu, 29 Oct 2020 22:26:15 +0100 Subject: [PATCH] Allow Customization of Bearer Token Resolution Closes gh-8535 --- ...wtIssuerAuthenticationManagerResolver.java | 23 +++++++++++++-- ...ReactiveAuthenticationManagerResolver.java | 26 +++++++++++++++-- ...uerAuthenticationManagerResolverTests.java | 28 +++++++++++++++++++ ...iveAuthenticationManagerResolverTests.java | 18 ++++++++++++ 4 files changed, 91 insertions(+), 4 deletions(-) diff --git a/oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/authentication/JwtIssuerAuthenticationManagerResolver.java b/oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/authentication/JwtIssuerAuthenticationManagerResolver.java index af1cddc205..f324d84f4d 100644 --- a/oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/authentication/JwtIssuerAuthenticationManagerResolver.java +++ b/oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/authentication/JwtIssuerAuthenticationManagerResolver.java @@ -65,7 +65,7 @@ public final class JwtIssuerAuthenticationManagerResolver implements Authenticat private final AuthenticationManagerResolver issuerAuthenticationManagerResolver; - private final Converter issuerConverter = new JwtClaimIssuerConverter(); + private Converter issuerConverter = new JwtClaimIssuerConverter(); /** * Construct a {@link JwtIssuerAuthenticationManagerResolver} using the provided @@ -130,9 +130,28 @@ public final class JwtIssuerAuthenticationManagerResolver implements Authenticat return authenticationManager; } + /** + * Set a custom bearer token resolver + * + * @since 5.5 + */ + public void setBearerTokenResolver(BearerTokenResolver bearerTokenResolver) { + Assert.notNull(bearerTokenResolver, "bearerTokenResolver cannot be null"); + this.issuerConverter = new JwtClaimIssuerConverter(bearerTokenResolver); + } + private static class JwtClaimIssuerConverter implements Converter { - private final BearerTokenResolver resolver = new DefaultBearerTokenResolver(); + private final BearerTokenResolver resolver; + + JwtClaimIssuerConverter() { + this(new DefaultBearerTokenResolver()); + } + + JwtClaimIssuerConverter(BearerTokenResolver bearerTokenResolver) { + Assert.notNull(bearerTokenResolver, "bearerTokenResolver cannot be null"); + this.resolver = bearerTokenResolver; + } @Override public String convert(@NonNull HttpServletRequest request) { diff --git a/oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/authentication/JwtIssuerReactiveAuthenticationManagerResolver.java b/oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/authentication/JwtIssuerReactiveAuthenticationManagerResolver.java index e73635e887..2db203df60 100644 --- a/oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/authentication/JwtIssuerReactiveAuthenticationManagerResolver.java +++ b/oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/authentication/JwtIssuerReactiveAuthenticationManagerResolver.java @@ -65,7 +65,7 @@ public final class JwtIssuerReactiveAuthenticationManagerResolver private final ReactiveAuthenticationManagerResolver issuerAuthenticationManagerResolver; - private final Converter> issuerConverter = new JwtClaimIssuerConverter(); + private Converter> issuerConverter = new JwtClaimIssuerConverter(); /** * Construct a {@link JwtIssuerReactiveAuthenticationManagerResolver} using the @@ -131,9 +131,31 @@ public final class JwtIssuerReactiveAuthenticationManagerResolver // @formatter:on } + /** + * Set a custom server bearer token authentication converter + * + * @since 5.5 + */ + public void setServerBearerTokenAuthenticationConverter( + ServerBearerTokenAuthenticationConverter serverBearerTokenAuthenticationConverter) { + Assert.notNull(serverBearerTokenAuthenticationConverter, + "serverBearerTokenAuthenticationConverter cannot be null"); + this.issuerConverter = new JwtClaimIssuerConverter(serverBearerTokenAuthenticationConverter); + } + private static class JwtClaimIssuerConverter implements Converter> { - private final ServerBearerTokenAuthenticationConverter converter = new ServerBearerTokenAuthenticationConverter(); + private final ServerBearerTokenAuthenticationConverter converter; + + JwtClaimIssuerConverter() { + this(new ServerBearerTokenAuthenticationConverter()); + } + + JwtClaimIssuerConverter(ServerBearerTokenAuthenticationConverter serverBearerTokenAuthenticationConverter) { + Assert.notNull(serverBearerTokenAuthenticationConverter, + "serverBearerTokenAuthenticationConverter cannot be null"); + this.converter = serverBearerTokenAuthenticationConverter; + } @Override public Mono convert(@NonNull ServerWebExchange exchange) { diff --git a/oauth2/oauth2-resource-server/src/test/java/org/springframework/security/oauth2/server/resource/authentication/JwtIssuerAuthenticationManagerResolverTests.java b/oauth2/oauth2-resource-server/src/test/java/org/springframework/security/oauth2/server/resource/authentication/JwtIssuerAuthenticationManagerResolverTests.java index c6d3a5397a..3fd3d3f498 100644 --- a/oauth2/oauth2-resource-server/src/test/java/org/springframework/security/oauth2/server/resource/authentication/JwtIssuerAuthenticationManagerResolverTests.java +++ b/oauth2/oauth2-resource-server/src/test/java/org/springframework/security/oauth2/server/resource/authentication/JwtIssuerAuthenticationManagerResolverTests.java @@ -21,6 +21,8 @@ import java.util.Collections; import java.util.HashMap; import java.util.Map; +import javax.servlet.http.HttpServletRequest; + import com.nimbusds.jose.JWSAlgorithm; import com.nimbusds.jose.JWSHeader; import com.nimbusds.jose.JWSObject; @@ -39,11 +41,15 @@ import org.springframework.security.authentication.AuthenticationManagerResolver import org.springframework.security.oauth2.core.OAuth2AuthenticationException; import org.springframework.security.oauth2.jose.TestKeys; import org.springframework.security.oauth2.jwt.JwtClaimNames; +import org.springframework.security.oauth2.server.resource.web.BearerTokenResolver; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; +import static org.mockito.Mockito.any; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; /** * Tests for {@link JwtIssuerAuthenticationManagerResolver} @@ -113,6 +119,19 @@ public class JwtIssuerAuthenticationManagerResolverTests { assertThat(authenticationManagerResolver.resolve(request)).isSameAs(authenticationManager); } + @Test + public void resolveWhenUsingCustomIssuerAuthenticationManagerResolverAndCustomBearerTokenResolverThenUses() { + AuthenticationManager authenticationManager = mock(AuthenticationManager.class); + JwtIssuerAuthenticationManagerResolver authenticationManagerResolver = new JwtIssuerAuthenticationManagerResolver( + (issuer) -> authenticationManager); + BearerTokenResolver bearerTokenResolverSpy = spy(new TestBearerTokenResolver()); + authenticationManagerResolver.setBearerTokenResolver(bearerTokenResolverSpy); + MockHttpServletRequest request = new MockHttpServletRequest(); + request.addHeader("Authorization", "Bearer " + this.jwt); + assertThat(authenticationManagerResolver.resolve(request)).isSameAs(authenticationManager); + verify(bearerTokenResolverSpy).resolve(any()); + } + @Test public void resolveWhenUsingExternalSourceThenRespondsToChanges() { MockHttpServletRequest request = new MockHttpServletRequest(); @@ -196,4 +215,13 @@ public class JwtIssuerAuthenticationManagerResolverTests { return jwt.serialize(); } + static class TestBearerTokenResolver implements BearerTokenResolver { + + @Override + public String resolve(HttpServletRequest request) { + return "eyJhbGciOiJub25lIn0.eyJpc3MiOiJ0cnVzdGVkIn0."; + } + + } + } diff --git a/oauth2/oauth2-resource-server/src/test/java/org/springframework/security/oauth2/server/resource/authentication/JwtIssuerReactiveAuthenticationManagerResolverTests.java b/oauth2/oauth2-resource-server/src/test/java/org/springframework/security/oauth2/server/resource/authentication/JwtIssuerReactiveAuthenticationManagerResolverTests.java index 02979d65bc..278096aaed 100644 --- a/oauth2/oauth2-resource-server/src/test/java/org/springframework/security/oauth2/server/resource/authentication/JwtIssuerReactiveAuthenticationManagerResolverTests.java +++ b/oauth2/oauth2-resource-server/src/test/java/org/springframework/security/oauth2/server/resource/authentication/JwtIssuerReactiveAuthenticationManagerResolverTests.java @@ -41,11 +41,15 @@ import org.springframework.security.authentication.ReactiveAuthenticationManager import org.springframework.security.oauth2.core.OAuth2AuthenticationException; import org.springframework.security.oauth2.jose.TestKeys; import org.springframework.security.oauth2.jwt.JwtClaimNames; +import org.springframework.security.oauth2.server.resource.web.server.ServerBearerTokenAuthenticationConverter; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; +import static org.mockito.Mockito.any; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; /** * Tests for {@link JwtIssuerReactiveAuthenticationManagerResolver} @@ -111,6 +115,20 @@ public class JwtIssuerReactiveAuthenticationManagerResolverTests { assertThat(authenticationManagerResolver.resolve(exchange).block()).isSameAs(authenticationManager); } + @Test + public void resolveWhenUsingCustomIssuerAuthenticationManagerResolverAndCustomServerBearerTokenAuthenticationConverterThenUses() { + ReactiveAuthenticationManager authenticationManager = mock(ReactiveAuthenticationManager.class); + JwtIssuerReactiveAuthenticationManagerResolver authenticationManagerResolver = new JwtIssuerReactiveAuthenticationManagerResolver( + (issuer) -> Mono.just(authenticationManager)); + ServerBearerTokenAuthenticationConverter serverBearerTokenAuthenticationConverterSpy = spy( + new ServerBearerTokenAuthenticationConverter()); + authenticationManagerResolver + .setServerBearerTokenAuthenticationConverter(serverBearerTokenAuthenticationConverterSpy); + MockServerWebExchange exchange = withBearerToken(this.jwt); + assertThat(authenticationManagerResolver.resolve(exchange).block()).isSameAs(authenticationManager); + verify(serverBearerTokenAuthenticationConverterSpy).convert(any()); + } + @Test public void resolveWhenUsingExternalSourceThenRespondsToChanges() { MockServerWebExchange exchange = withBearerToken(this.jwt);