Forráskód Böngészése

ServerAuthenticationConverter should be configurable

Fixes gh-6186
Eric Deandrea 6 éve
szülő
commit
be423debfd

+ 21 - 8
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
+		 * <a href="https://tools.ietf.org/html/rfc6750#section-1.2" target="_blank">Bearer Token</a>s.
+		 *
+		 * @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<MatchResult> 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;
 				}

+ 51 - 10
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 {