diff --git a/config/src/main/java/org/springframework/security/config/web/server/ServerHttpSecurity.java b/config/src/main/java/org/springframework/security/config/web/server/ServerHttpSecurity.java
index 848f6e53cd..7021e05de3 100644
--- a/config/src/main/java/org/springframework/security/config/web/server/ServerHttpSecurity.java
+++ b/config/src/main/java/org/springframework/security/config/web/server/ServerHttpSecurity.java
@@ -16,6 +16,10 @@
package org.springframework.security.config.web.server;
+import static org.springframework.security.web.server.DelegatingServerAuthenticationEntryPoint.DelegateEntry;
+import static org.springframework.security.web.server.util.matcher.ServerWebExchangeMatcher.MatchResult.match;
+import static org.springframework.security.web.server.util.matcher.ServerWebExchangeMatcher.MatchResult.notMatch;
+
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
@@ -155,10 +159,6 @@ import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebFilter;
import org.springframework.web.server.WebFilterChain;
-import static org.springframework.security.web.server.DelegatingServerAuthenticationEntryPoint.DelegateEntry;
-import static org.springframework.security.web.server.util.matcher.ServerWebExchangeMatcher.MatchResult.match;
-import static org.springframework.security.web.server.util.matcher.ServerWebExchangeMatcher.MatchResult.notMatch;
-
/**
* A {@link ServerHttpSecurity} is similar to Spring Security's {@code HttpSecurity} but for WebFlux.
* It allows configuring web based security for specific http requests. By default it will be applied
@@ -883,6 +883,7 @@ public class ServerHttpSecurity {
public class OAuth2ResourceServerSpec {
private ServerAuthenticationEntryPoint entryPoint = new BearerTokenServerAuthenticationEntryPoint();
private ServerAccessDeniedHandler accessDeniedHandler = new BearerTokenServerAccessDeniedHandler();
+ private ServerAuthenticationConverter bearerTokenConverter = new ServerBearerTokenAuthenticationConverter();
private JwtSpec jwt;
@@ -915,6 +916,20 @@ public class ServerHttpSecurity {
return this;
}
+ /**
+ * Configures the {@link ServerAuthenticationConverter} to use for requests authenticating with
+ * Bearer Tokens.
+ *
+ * @param bearerTokenConverter The {@link ServerAuthenticationConverter} to use
+ * @return The {@link OAuth2ResourceServerSpec} for additional configuration
+ * @since 5.2
+ */
+ public OAuth2ResourceServerSpec bearerTokenConverter(ServerAuthenticationConverter bearerTokenConverter) {
+ Assert.notNull(bearerTokenConverter, "bearerTokenConverter cannot be null");
+ this.bearerTokenConverter = bearerTokenConverter;
+ return this;
+ }
+
public JwtSpec jwt() {
if (this.jwt == null) {
this.jwt = new JwtSpec();
@@ -1003,8 +1018,6 @@ public class ServerHttpSecurity {
}
protected void configure(ServerHttpSecurity http) {
- ServerBearerTokenAuthenticationConverter bearerTokenConverter =
- new ServerBearerTokenAuthenticationConverter();
this.bearerTokenServerWebExchangeMatcher.setBearerTokenConverter(bearerTokenConverter);
registerDefaultAccessDeniedHandler(http);
@@ -1083,7 +1096,7 @@ public class ServerHttpSecurity {
}
private class BearerTokenServerWebExchangeMatcher implements ServerWebExchangeMatcher {
- ServerBearerTokenAuthenticationConverter bearerTokenConverter;
+ ServerAuthenticationConverter bearerTokenConverter;
@Override
public Mono matches(ServerWebExchange exchange) {
@@ -1092,7 +1105,7 @@ public class ServerHttpSecurity {
.onErrorResume(e -> notMatch());
}
- public void setBearerTokenConverter(ServerBearerTokenAuthenticationConverter bearerTokenConverter) {
+ public void setBearerTokenConverter(ServerAuthenticationConverter bearerTokenConverter) {
Assert.notNull(bearerTokenConverter, "bearerTokenConverter cannot be null");
this.bearerTokenConverter = bearerTokenConverter;
}
diff --git a/config/src/test/java/org/springframework/security/config/web/server/OAuth2ResourceServerSpecTests.java b/config/src/test/java/org/springframework/security/config/web/server/OAuth2ResourceServerSpecTests.java
index f301b4a5a0..2409b1b889 100644
--- a/config/src/test/java/org/springframework/security/config/web/server/OAuth2ResourceServerSpecTests.java
+++ b/config/src/test/java/org/springframework/security/config/web/server/OAuth2ResourceServerSpecTests.java
@@ -15,6 +15,15 @@
*/
package org.springframework.security.config.web.server;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatCode;
+import static org.hamcrest.core.StringStartsWith.startsWith;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
import java.io.IOException;
import java.math.BigInteger;
import java.security.KeyFactory;
@@ -27,14 +36,14 @@ import java.util.Collection;
import java.util.Collections;
import java.util.stream.Collectors;
import java.util.stream.Stream;
+
import javax.annotation.PreDestroy;
-import okhttp3.mockwebserver.MockResponse;
-import okhttp3.mockwebserver.MockWebServer;
import org.apache.http.HttpHeaders;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
+
import reactor.core.publisher.Mono;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
@@ -56,10 +65,12 @@ import org.springframework.security.oauth2.core.OAuth2Error;
import org.springframework.security.oauth2.jose.jws.JwsAlgorithms;
import org.springframework.security.oauth2.jwt.Jwt;
import org.springframework.security.oauth2.jwt.ReactiveJwtDecoder;
+import org.springframework.security.oauth2.server.resource.BearerTokenAuthenticationToken;
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationConverter;
import org.springframework.security.oauth2.server.resource.authentication.ReactiveJwtAuthenticationConverterAdapter;
import org.springframework.security.web.server.SecurityWebFilterChain;
import org.springframework.security.web.server.authentication.HttpStatusServerEntryPoint;
+import org.springframework.security.web.server.authentication.ServerAuthenticationConverter;
import org.springframework.security.web.server.authorization.HttpStatusServerAccessDeniedHandler;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.reactive.server.WebTestClient;
@@ -70,14 +81,8 @@ import org.springframework.web.context.support.GenericWebApplicationContext;
import org.springframework.web.reactive.DispatcherHandler;
import org.springframework.web.reactive.config.EnableWebFlux;
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.assertj.core.api.Assertions.assertThatCode;
-import static org.hamcrest.core.StringStartsWith.startsWith;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
+import okhttp3.mockwebserver.MockResponse;
+import okhttp3.mockwebserver.MockWebServer;
/**
* Tests for {@link org.springframework.security.config.web.server.ServerHttpSecurity.OAuth2ResourceServerSpec}
@@ -225,6 +230,16 @@ public class OAuth2ResourceServerSpecTests {
.expectStatus().isForbidden();
}
+ @Test
+ public void getWhenCustomBearerTokenServerAuthenticationConverterThenResponds() {
+ this.spring.register(CustomBearerTokenServerAuthenticationConverter.class, RootController.class).autowire();
+
+ this.client.get()
+ .cookie("TOKEN", this.messageReadToken)
+ .exchange()
+ .expectStatus().isOk();
+ }
+
@Test
public void getWhenSignedAndCustomConverterThenConverts() {
this.spring.register(CustomJwtAuthenticationConverterConfig.class, RootController.class).autowire();
@@ -429,6 +444,32 @@ public class OAuth2ResourceServerSpecTests {
}
}
+ @EnableWebFlux
+ @EnableWebFluxSecurity
+ static class CustomBearerTokenServerAuthenticationConverter {
+ @Bean
+ SecurityWebFilterChain springSecurity(ServerHttpSecurity http) throws Exception {
+ // @formatter:off
+ http
+ .authorizeExchange()
+ .anyExchange().hasAuthority("SCOPE_message:read")
+ .and()
+ .oauth2ResourceServer()
+ .bearerTokenConverter(bearerTokenAuthenticationConverter())
+ .jwt()
+ .publicKey(publicKey());
+ // @formatter:on
+
+ return http.build();
+ }
+
+ @Bean
+ ServerAuthenticationConverter bearerTokenAuthenticationConverter() {
+ return exchange -> Mono.justOrEmpty(exchange.getRequest().getCookies().getFirst("TOKEN").getValue())
+ .map(BearerTokenAuthenticationToken::new);
+ }
+ }
+
@EnableWebFlux
@EnableWebFluxSecurity
static class CustomJwtAuthenticationConverterConfig {