diff --git a/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/filter/ratelimit/PrincipalNameKeyResolverIntegrationTests.java b/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/filter/ratelimit/PrincipalNameKeyResolverIntegrationTests.java index c72a0c2c5..2447d573f 100644 --- a/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/filter/ratelimit/PrincipalNameKeyResolverIntegrationTests.java +++ b/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/filter/ratelimit/PrincipalNameKeyResolverIntegrationTests.java @@ -37,9 +37,6 @@ import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Primary; import org.springframework.security.config.web.server.ServerHttpSecurity; -import org.springframework.security.core.userdetails.MapReactiveUserDetailsService; -import org.springframework.security.core.userdetails.User; -import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.web.server.SecurityWebFilterChain; import org.springframework.test.context.ActiveProfiles; import org.springframework.test.util.TestSocketUtils; @@ -52,7 +49,9 @@ import org.springframework.web.bind.annotation.RestController; import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.DEFINED_PORT; import static org.springframework.web.reactive.function.client.ExchangeFilterFunctions.basicAuthentication; -@SpringBootTest(webEnvironment = DEFINED_PORT) +@SpringBootTest(webEnvironment = DEFINED_PORT, + properties = { "spring.security.user.name=user", "spring.security.user.password={noop}password", + "spring.security.user.roles=USER" }) @ActiveProfiles("principalname") public class PrincipalNameKeyResolverIntegrationTests { @@ -120,12 +119,6 @@ public class PrincipalNameKeyResolverIntegrationTests { .permitAll().and().build(); } - @Bean - public MapReactiveUserDetailsService reactiveUserDetailsService() { - UserDetails user = User.withUsername("user").password("{noop}password").roles("USER").build(); - return new MapReactiveUserDetailsService(user); - } - class MyRateLimiter implements RateLimiter { private HashMap map = new HashMap<>(); diff --git a/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/test/PermitAllSecurityConfiguration.java b/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/test/PermitAllSecurityConfiguration.java index 3a2c0bb2e..78594f8df 100644 --- a/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/test/PermitAllSecurityConfiguration.java +++ b/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/test/PermitAllSecurityConfiguration.java @@ -18,14 +18,10 @@ package org.springframework.cloud.gateway.test; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity; import org.springframework.security.config.web.server.ServerHttpSecurity; import org.springframework.security.web.server.SecurityWebFilterChain; @Configuration(proxyBeanMethods = false) -// TODO: remove when below is fixed -// https://github.com/spring-projects/spring-boot/commit/ee9c74556d45b39f097aff8a5fa65c6394d060b5 -@EnableWebFluxSecurity public class PermitAllSecurityConfiguration { @Bean diff --git a/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/test/TestEnableWebfluxSecurityAutoConfiguration.java b/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/test/TestEnableWebfluxSecurityAutoConfiguration.java new file mode 100644 index 000000000..1c5622682 --- /dev/null +++ b/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/test/TestEnableWebfluxSecurityAutoConfiguration.java @@ -0,0 +1,81 @@ +/* + * Copyright 2013-2023 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.cloud.gateway.test; + +import java.util.List; +import java.util.regex.Pattern; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import org.springframework.beans.factory.ObjectProvider; +import org.springframework.boot.autoconfigure.AutoConfiguration; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.security.SecurityProperties; +import org.springframework.boot.autoconfigure.security.reactive.ReactiveSecurityAutoConfiguration; +import org.springframework.context.annotation.Bean; +import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity; +import org.springframework.security.core.userdetails.MapReactiveUserDetailsService; +import org.springframework.security.core.userdetails.User; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.util.StringUtils; +import org.springframework.web.reactive.DispatcherHandler; + +/* + See https://github.com/spring-projects/spring-boot/issues/37504. + Because gateway has normal spring security and spring security oauth 2 + we need an explicit @EnableWebFluxSecurity and a ReactiveUserDetailsService + */ +@AutoConfiguration(before = ReactiveSecurityAutoConfiguration.class) +@ConditionalOnClass(DispatcherHandler.class) +@EnableWebFluxSecurity +public class TestEnableWebfluxSecurityAutoConfiguration { + + private static final String NOOP_PASSWORD_PREFIX = "{noop}"; + + private static final Pattern PASSWORD_ALGORITHM_PATTERN = Pattern.compile("^\\{.+}.*$"); + + private static final Log logger = LogFactory.getLog(TestEnableWebfluxSecurityAutoConfiguration.class); + + @Bean + @ConditionalOnMissingBean + public MapReactiveUserDetailsService reactiveUserDetailsService(SecurityProperties properties, + ObjectProvider passwordEncoder) { + SecurityProperties.User user = properties.getUser(); + UserDetails userDetails = getUserDetails(user, getOrDeducePassword(user, passwordEncoder.getIfAvailable())); + return new MapReactiveUserDetailsService(userDetails); + } + + private UserDetails getUserDetails(SecurityProperties.User user, String password) { + List roles = user.getRoles(); + return User.withUsername(user.getName()).password(password).roles(StringUtils.toStringArray(roles)).build(); + } + + private String getOrDeducePassword(SecurityProperties.User user, PasswordEncoder encoder) { + String password = user.getPassword(); + if (user.isPasswordGenerated()) { + logger.info(String.format("%n%nUsing generated security password: %s%n", user.getPassword())); + } + if (encoder != null || PASSWORD_ALGORITHM_PATTERN.matcher(password).matches()) { + return password; + } + return NOOP_PASSWORD_PREFIX + password; + } + +} diff --git a/spring-cloud-gateway-server/src/test/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/spring-cloud-gateway-server/src/test/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 000000000..2b1223337 --- /dev/null +++ b/spring-cloud-gateway-server/src/test/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -0,0 +1 @@ +org.springframework.cloud.gateway.test.TestEnableWebfluxSecurityAutoConfiguration \ No newline at end of file