This commit is contained in:
Phillip Webb 2017-12-21 19:12:05 -08:00
parent 6eabe8235c
commit ba86b68484
9 changed files with 213 additions and 163 deletions

View File

@ -51,33 +51,38 @@ import org.springframework.security.provisioning.InMemoryUserDetailsManager;
UserDetailsService.class }) UserDetailsService.class })
public class AuthenticationManagerConfiguration { public class AuthenticationManagerConfiguration {
private final Pattern pattern = Pattern.compile("^\\{.+}.*$"); private static final String NOOP_PASSWORD_PREFIX = "{noop}";
private static final Pattern PASSWORD_ALGORITHM_PATTERN = Pattern
.compile("^\\{.+}.*$");
private static final Log logger = LogFactory private static final Log logger = LogFactory
.getLog(AuthenticationManagerConfiguration.class); .getLog(AuthenticationManagerConfiguration.class);
private static final String NOOP_PREFIX = "{noop}";
@Bean @Bean
public InMemoryUserDetailsManager inMemoryUserDetailsManager(SecurityProperties properties, public InMemoryUserDetailsManager inMemoryUserDetailsManager(
SecurityProperties properties,
ObjectProvider<PasswordEncoder> passwordEncoder) throws Exception { ObjectProvider<PasswordEncoder> passwordEncoder) throws Exception {
SecurityProperties.User user = properties.getUser(); SecurityProperties.User user = properties.getUser();
if (user.isPasswordGenerated()) {
logger.info(String.format("%n%nUsing generated security password: %s%n", user.getPassword()));
}
String password = deducePassword(passwordEncoder, user.getPassword());
List<String> roles = user.getRoles(); List<String> roles = user.getRoles();
return new InMemoryUserDetailsManager( return new InMemoryUserDetailsManager(
User.withUsername(user.getName()).password(password) User.withUsername(user.getName())
.roles(roles.toArray(new String[roles.size()])).build()); .password(getOrDeducePassword(user,
passwordEncoder.getIfAvailable()))
.roles(roles.toArray(new String[roles.size()])).build());
} }
private String deducePassword(ObjectProvider<PasswordEncoder> passwordEncoder, String password) { public String getOrDeducePassword(SecurityProperties.User user,
if (passwordEncoder.getIfAvailable() == null && PasswordEncoder encoder) {
!this.pattern.matcher(password).matches()) { String password = user.getPassword();
return NOOP_PREFIX + password; if (user.isPasswordGenerated()) {
logger.info(String.format("%n%nUsing generated security password: %s%n",
user.getPassword()));
} }
return password; if (encoder != null || PASSWORD_ALGORITHM_PATTERN.matcher(password).matches()) {
return password;
}
return NOOP_PASSWORD_PREFIX + password;
} }
} }

View File

@ -51,9 +51,10 @@ import org.springframework.security.crypto.password.PasswordEncoder;
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.REACTIVE) @ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.REACTIVE)
class ReactiveAuthenticationManagerConfiguration { class ReactiveAuthenticationManagerConfiguration {
private final Pattern pattern = Pattern.compile("^\\{.+}.*$"); private static final String NOOP_PASSWORD_PREFIX = "{noop}";
private static final String NOOP_PREFIX = "{noop}"; private static final Pattern PASSWORD_ALGORITHM_PATTERN = Pattern
.compile("^\\{.+}.*$");
private static final Log logger = LogFactory private static final Log logger = LogFactory
.getLog(ReactiveAuthenticationManagerConfiguration.class); .getLog(ReactiveAuthenticationManagerConfiguration.class);
@ -63,28 +64,28 @@ class ReactiveAuthenticationManagerConfiguration {
SecurityProperties properties, SecurityProperties properties,
ObjectProvider<PasswordEncoder> passwordEncoder) { ObjectProvider<PasswordEncoder> passwordEncoder) {
SecurityProperties.User user = properties.getUser(); SecurityProperties.User user = properties.getUser();
if (user.isPasswordGenerated()) { UserDetails userDetails = getUserDetails(user,
logger.info(String.format("%n%nUsing default security password: %s%n", getOrDeducePassword(user, passwordEncoder.getIfAvailable()));
user.getPassword()));
}
String password = deducePassword(passwordEncoder, user.getPassword());
UserDetails userDetails = getUserDetails(user, password);
return new MapReactiveUserDetailsService(userDetails); return new MapReactiveUserDetailsService(userDetails);
} }
private String deducePassword(ObjectProvider<PasswordEncoder> passwordEncoder, String password) { private UserDetails getUserDetails(SecurityProperties.User user, String password) {
if (passwordEncoder.getIfAvailable() == null &&
!this.pattern.matcher(password).matches()) {
return NOOP_PREFIX + password;
}
return password;
}
private UserDetails getUserDetails(SecurityProperties.User user,
String password) {
List<String> roles = user.getRoles(); List<String> roles = user.getRoles();
return User.withUsername(user.getName()).password(password) return User.withUsername(user.getName()).password(password)
.roles(roles.toArray(new String[roles.size()])).build(); .roles(roles.toArray(new String[roles.size()])).build();
} }
private String getOrDeducePassword(SecurityProperties.User user,
PasswordEncoder encoder) {
String password = user.getPassword();
if (user.isPasswordGenerated()) {
logger.info(String.format("%n%nUsing default security password: %s%n",
user.getPassword()));
}
if (encoder != null || PASSWORD_ALGORITHM_PATTERN.matcher(password).matches()) {
return password;
}
return NOOP_PASSWORD_PREFIX + password;
}
} }

View File

@ -34,35 +34,35 @@ import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
/** /**
* {@link org.springframework.format.support.FormattingConversionService} dedicated * {@link org.springframework.format.support.FormattingConversionService} dedicated to web
* to web applications for formatting and converting values to/from the web. * applications for formatting and converting values to/from the web.
* * <p>
* <p>This service replaces the default implementations provided by * This service replaces the default implementations provided by
* {@link org.springframework.web.servlet.config.annotation.EnableWebMvc} * {@link org.springframework.web.servlet.config.annotation.EnableWebMvc} and
* and {@link org.springframework.web.reactive.config.EnableWebFlux}. * {@link org.springframework.web.reactive.config.EnableWebFlux}.
* *
* @author Brian Clozel * @author Brian Clozel
* @since 2.0.0 * @since 2.0.0
*/ */
public class WebConversionService extends DefaultFormattingConversionService { public class WebConversionService extends DefaultFormattingConversionService {
private static final boolean jsr354Present = ClassUtils private static final boolean jsr354Present = ClassUtils.isPresent(
.isPresent("javax.money.MonetaryAmount", WebConversionService.class.getClassLoader()); "javax.money.MonetaryAmount", WebConversionService.class.getClassLoader());
private static final boolean jodaTimePresent = ClassUtils private static final boolean jodaTimePresent = ClassUtils.isPresent(
.isPresent("org.joda.time.LocalDate", WebConversionService.class.getClassLoader()); "org.joda.time.LocalDate", WebConversionService.class.getClassLoader());
private String dateFormat; private final String dateFormat;
/** /**
* Create a new WebConversionService that configures formatters with the provided date format, * Create a new WebConversionService that configures formatters with the provided date
* or register the default ones if no custom format is provided. * format, or register the default ones if no custom format is provided.
* @param dateFormat the custom date format to use for date conversions * @param dateFormat the custom date format to use for date conversions
*/ */
public WebConversionService(String dateFormat) { public WebConversionService(String dateFormat) {
super(false); super(false);
if (StringUtils.hasText(dateFormat)) { this.dateFormat = (StringUtils.hasText(dateFormat) ? dateFormat : null);
this.dateFormat = dateFormat; if (this.dateFormat != null) {
addFormatters(); addFormatters();
} }
else { else {
@ -70,13 +70,13 @@ public class WebConversionService extends DefaultFormattingConversionService {
} }
} }
private void addFormatters() { private void addFormatters() {
addFormatterForFieldAnnotation(new NumberFormatAnnotationFormatterFactory()); addFormatterForFieldAnnotation(new NumberFormatAnnotationFormatterFactory());
if (jsr354Present) { if (jsr354Present) {
addFormatter(new CurrencyUnitFormatter()); addFormatter(new CurrencyUnitFormatter());
addFormatter(new MonetaryAmountFormatter()); addFormatter(new MonetaryAmountFormatter());
addFormatterForFieldAnnotation(new Jsr354NumberFormatAnnotationFormatterFactory()); addFormatterForFieldAnnotation(
new Jsr354NumberFormatAnnotationFormatterFactory());
} }
registerJsr310(); registerJsr310();
if (jodaTimePresent) { if (jodaTimePresent) {
@ -88,10 +88,8 @@ public class WebConversionService extends DefaultFormattingConversionService {
private void registerJsr310() { private void registerJsr310() {
DateTimeFormatterRegistrar dateTime = new DateTimeFormatterRegistrar(); DateTimeFormatterRegistrar dateTime = new DateTimeFormatterRegistrar();
if (this.dateFormat != null) { if (this.dateFormat != null) {
DateTimeFormatter dateTimeFormatter = DateTimeFormatter dateTime.setDateFormatter(DateTimeFormatter.ofPattern(this.dateFormat)
.ofPattern(this.dateFormat) .withResolverStyle(ResolverStyle.STRICT));
.withResolverStyle(ResolverStyle.STRICT);
dateTime.setDateFormatter(dateTimeFormatter);
} }
dateTime.registerFormatters(this); dateTime.registerFormatters(this);
} }
@ -99,10 +97,8 @@ public class WebConversionService extends DefaultFormattingConversionService {
private void registerJodaTime() { private void registerJodaTime() {
JodaTimeFormatterRegistrar jodaTime = new JodaTimeFormatterRegistrar(); JodaTimeFormatterRegistrar jodaTime = new JodaTimeFormatterRegistrar();
if (this.dateFormat != null) { if (this.dateFormat != null) {
org.joda.time.format.DateTimeFormatter dateTimeFormatter = new DateTimeFormatterBuilder() jodaTime.setDateFormatter(new DateTimeFormatterBuilder()
.appendPattern(this.dateFormat) .appendPattern(this.dateFormat).toFormatter());
.toFormatter();
jodaTime.setDateFormatter(dateTimeFormatter);
} }
jodaTime.registerFormatters(this); jodaTime.registerFormatters(this);
} }
@ -115,4 +111,5 @@ public class WebConversionService extends DefaultFormattingConversionService {
} }
dateFormatterRegistrar.registerFormatters(this); dateFormatterRegistrar.registerFormatters(this);
} }
} }

View File

@ -223,7 +223,8 @@ public class WebFluxAutoConfiguration {
@Bean @Bean
@Override @Override
public FormattingConversionService webFluxConversionService() { public FormattingConversionService webFluxConversionService() {
WebConversionService conversionService = new WebConversionService(this.webFluxProperties.getDateFormat()); WebConversionService conversionService = new WebConversionService(
this.webFluxProperties.getDateFormat());
addFormatters(conversionService); addFormatters(conversionService);
return conversionService; return conversionService;
} }

View File

@ -301,8 +301,8 @@ public class WebMvcAutoConfiguration {
return; return;
} }
Duration cachePeriod = this.resourceProperties.getCache().getPeriod(); Duration cachePeriod = this.resourceProperties.getCache().getPeriod();
CacheControl cacheControl = this.resourceProperties.getCache().getCachecontrol() CacheControl cacheControl = this.resourceProperties.getCache()
.toHttpCacheControl(); .getCachecontrol().toHttpCacheControl();
if (!registry.hasMappingForPattern("/webjars/**")) { if (!registry.hasMappingForPattern("/webjars/**")) {
customizeResourceHandlerRegistration( customizeResourceHandlerRegistration(
registry.addResourceHandler("/webjars/**") registry.addResourceHandler("/webjars/**")
@ -475,7 +475,8 @@ public class WebMvcAutoConfiguration {
@Bean @Bean
@Override @Override
public FormattingConversionService mvcConversionService() { public FormattingConversionService mvcConversionService() {
WebConversionService conversionService = new WebConversionService(this.mvcProperties.getDateFormat()); WebConversionService conversionService = new WebConversionService(
this.mvcProperties.getDateFormat());
addFormatters(conversionService); addFormatters(conversionService);
return conversionService; return conversionService;
} }

View File

@ -40,22 +40,27 @@ public class AuthenticationManagerConfigurationTests {
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner(); private final ApplicationContextRunner contextRunner = new ApplicationContextRunner();
@Test @Test
public void userDetailsServiceWhenPasswordEncoderAbsentAndDefaultPassword() throws Exception { public void userDetailsServiceWhenPasswordEncoderAbsentAndDefaultPassword()
throws Exception {
this.contextRunner.withUserConfiguration(TestSecurityConfiguration.class, this.contextRunner.withUserConfiguration(TestSecurityConfiguration.class,
AuthenticationManagerConfiguration.class).run((context -> { AuthenticationManagerConfiguration.class).run((context -> {
InMemoryUserDetailsManager userDetailsService = context.getBean(InMemoryUserDetailsManager.class); InMemoryUserDetailsManager userDetailsService = context
String password = userDetailsService.loadUserByUsername("user").getPassword(); .getBean(InMemoryUserDetailsManager.class);
assertThat(password).startsWith("{noop}"); String password = userDetailsService.loadUserByUsername("user")
})); .getPassword();
assertThat(password).startsWith("{noop}");
}));
} }
@Test @Test
public void userDetailsServiceWhenPasswordEncoderAbsentAndRawPassword() throws Exception { public void userDetailsServiceWhenPasswordEncoderAbsentAndRawPassword()
throws Exception {
testPasswordEncoding(TestSecurityConfiguration.class, "secret", "{noop}secret"); testPasswordEncoding(TestSecurityConfiguration.class, "secret", "{noop}secret");
} }
@Test @Test
public void userDetailsServiceWhenPasswordEncoderAbsentAndEncodedPassword() throws Exception { public void userDetailsServiceWhenPasswordEncoderAbsentAndEncodedPassword()
throws Exception {
String password = "{bcrypt}$2a$10$sCBi9fy9814vUPf2ZRbtp.fR5/VgRk2iBFZ.ypu5IyZ28bZgxrVDa"; String password = "{bcrypt}$2a$10$sCBi9fy9814vUPf2ZRbtp.fR5/VgRk2iBFZ.ypu5IyZ28bZgxrVDa";
testPasswordEncoding(TestSecurityConfiguration.class, password, password); testPasswordEncoding(TestSecurityConfiguration.class, password, password);
} }
@ -65,14 +70,19 @@ public class AuthenticationManagerConfigurationTests {
testPasswordEncoding(TestConfigWithPasswordEncoder.class, "secret", "secret"); testPasswordEncoding(TestConfigWithPasswordEncoder.class, "secret", "secret");
} }
private void testPasswordEncoding(Class<?> configClass, String providedPassword, String expectedPassword) { private void testPasswordEncoding(Class<?> configClass, String providedPassword,
this.contextRunner.withUserConfiguration(configClass, String expectedPassword) {
AuthenticationManagerConfiguration.class) this.contextRunner
.withPropertyValues("spring.security.user.password=" + providedPassword).run((context -> { .withUserConfiguration(configClass,
InMemoryUserDetailsManager userDetailsService = context.getBean(InMemoryUserDetailsManager.class); AuthenticationManagerConfiguration.class)
String password = userDetailsService.loadUserByUsername("user").getPassword(); .withPropertyValues("spring.security.user.password=" + providedPassword)
assertThat(password).isEqualTo(expectedPassword); .run((context -> {
})); InMemoryUserDetailsManager userDetailsService = context
.getBean(InMemoryUserDetailsManager.class);
String password = userDetailsService.loadUserByUsername("user")
.getPassword();
assertThat(password).isEqualTo(expectedPassword);
}));
} }
@Configuration @Configuration
@ -92,4 +102,5 @@ public class AuthenticationManagerConfigurationTests {
} }
} }
} }

View File

@ -41,22 +41,29 @@ public class ReactiveAuthenticationManagerConfigurationTests {
private final ReactiveWebApplicationContextRunner contextRunner = new ReactiveWebApplicationContextRunner(); private final ReactiveWebApplicationContextRunner contextRunner = new ReactiveWebApplicationContextRunner();
@Test @Test
public void userDetailsServiceWhenPasswordEncoderAbsentAndDefaultPassword() throws Exception { public void userDetailsServiceWhenPasswordEncoderAbsentAndDefaultPassword()
this.contextRunner.withUserConfiguration(TestSecurityConfiguration.class, throws Exception {
ReactiveAuthenticationManagerConfiguration.class).run((context -> { this.contextRunner
MapReactiveUserDetailsService userDetailsService = context.getBean(MapReactiveUserDetailsService.class); .withUserConfiguration(TestSecurityConfiguration.class,
String password = userDetailsService.findByUsername("user").block().getPassword(); ReactiveAuthenticationManagerConfiguration.class)
assertThat(password).startsWith("{noop}"); .run((context -> {
})); MapReactiveUserDetailsService userDetailsService = context
.getBean(MapReactiveUserDetailsService.class);
String password = userDetailsService.findByUsername("user").block()
.getPassword();
assertThat(password).startsWith("{noop}");
}));
} }
@Test @Test
public void userDetailsServiceWhenPasswordEncoderAbsentAndRawPassword() throws Exception { public void userDetailsServiceWhenPasswordEncoderAbsentAndRawPassword()
throws Exception {
testPasswordEncoding(TestSecurityConfiguration.class, "secret", "{noop}secret"); testPasswordEncoding(TestSecurityConfiguration.class, "secret", "{noop}secret");
} }
@Test @Test
public void userDetailsServiceWhenPasswordEncoderAbsentAndEncodedPassword() throws Exception { public void userDetailsServiceWhenPasswordEncoderAbsentAndEncodedPassword()
throws Exception {
String password = "{bcrypt}$2a$10$sCBi9fy9814vUPf2ZRbtp.fR5/VgRk2iBFZ.ypu5IyZ28bZgxrVDa"; String password = "{bcrypt}$2a$10$sCBi9fy9814vUPf2ZRbtp.fR5/VgRk2iBFZ.ypu5IyZ28bZgxrVDa";
testPasswordEncoding(TestSecurityConfiguration.class, password, password); testPasswordEncoding(TestSecurityConfiguration.class, password, password);
} }
@ -66,14 +73,19 @@ public class ReactiveAuthenticationManagerConfigurationTests {
testPasswordEncoding(TestConfigWithPasswordEncoder.class, "secret", "secret"); testPasswordEncoding(TestConfigWithPasswordEncoder.class, "secret", "secret");
} }
private void testPasswordEncoding(Class<?> configClass, String providedPassword, String expectedPassword) { private void testPasswordEncoding(Class<?> configClass, String providedPassword,
this.contextRunner.withUserConfiguration(configClass, String expectedPassword) {
ReactiveAuthenticationManagerConfiguration.class) this.contextRunner
.withPropertyValues("spring.security.user.password=" + providedPassword).run((context -> { .withUserConfiguration(configClass,
MapReactiveUserDetailsService userDetailsService = context.getBean(MapReactiveUserDetailsService.class); ReactiveAuthenticationManagerConfiguration.class)
String password = userDetailsService.findByUsername("user").block().getPassword(); .withPropertyValues("spring.security.user.password=" + providedPassword)
assertThat(password).isEqualTo(expectedPassword); .run((context -> {
})); MapReactiveUserDetailsService userDetailsService = context
.getBean(MapReactiveUserDetailsService.class);
String password = userDetailsService.findByUsername("user").block()
.getPassword();
assertThat(password).isEqualTo(expectedPassword);
}));
} }
@Configuration @Configuration

View File

@ -34,17 +34,14 @@ public class WebConversionServiceTests {
@Test @Test
public void customDateFormat() { public void customDateFormat() {
WebConversionService conversionService = new WebConversionService("dd*MM*yyyy"); WebConversionService conversionService = new WebConversionService("dd*MM*yyyy");
Date date = new DateTime(2018, 1, 1, 20, 30).toDate(); Date date = new DateTime(2018, 1, 1, 20, 30).toDate();
assertThat(conversionService.convert(date, String.class)) assertThat(conversionService.convert(date, String.class)).isEqualTo("01*01*2018");
.isEqualTo("01*01*2018");
LocalDate jodaDate = LocalDate.fromDateFields(date); LocalDate jodaDate = LocalDate.fromDateFields(date);
assertThat(conversionService.convert(jodaDate, String.class)) assertThat(conversionService.convert(jodaDate, String.class))
.isEqualTo("01*01*2018"); .isEqualTo("01*01*2018");
java.time.LocalDate localDate = java.time.LocalDate.of(2018, 1, 1); java.time.LocalDate localDate = java.time.LocalDate.of(2018, 1, 1);
assertThat(conversionService.convert(localDate, String.class)) assertThat(conversionService.convert(localDate, String.class))
.isEqualTo("01*01*2018"); .isEqualTo("01*01*2018");
} }
} }

View File

@ -69,8 +69,7 @@ import static org.mockito.Mockito.verify;
*/ */
public class WebFluxAutoConfigurationTests { public class WebFluxAutoConfigurationTests {
private static final MockReactiveWebServerFactory mockReactiveWebServerFactory private static final MockReactiveWebServerFactory mockReactiveWebServerFactory = new MockReactiveWebServerFactory();
= new MockReactiveWebServerFactory();
private ReactiveWebApplicationContextRunner contextRunner = new ReactiveWebApplicationContextRunner() private ReactiveWebApplicationContextRunner contextRunner = new ReactiveWebApplicationContextRunner()
.withConfiguration(AutoConfigurations.of(WebFluxAutoConfiguration.class)) .withConfiguration(AutoConfigurations.of(WebFluxAutoConfiguration.class))
@ -80,8 +79,10 @@ public class WebFluxAutoConfigurationTests {
public void shouldNotProcessIfExistingWebReactiveConfiguration() { public void shouldNotProcessIfExistingWebReactiveConfiguration() {
this.contextRunner.withUserConfiguration(WebFluxConfigurationSupport.class) this.contextRunner.withUserConfiguration(WebFluxConfigurationSupport.class)
.run(context -> { .run(context -> {
assertThat(context).getBeans(RequestMappingHandlerMapping.class).hasSize(1); assertThat(context).getBeans(RequestMappingHandlerMapping.class)
assertThat(context).getBeans(RequestMappingHandlerAdapter.class).hasSize(1); .hasSize(1);
assertThat(context).getBeans(RequestMappingHandlerAdapter.class)
.hasSize(1);
}); });
} }
@ -99,28 +100,30 @@ public class WebFluxAutoConfigurationTests {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@Test @Test
public void shouldRegisterCustomHandlerMethodArgumentResolver() { public void shouldRegisterCustomHandlerMethodArgumentResolver() {
this.contextRunner.withUserConfiguration(CustomArgumentResolvers.class).run(context -> { this.contextRunner.withUserConfiguration(CustomArgumentResolvers.class)
RequestMappingHandlerAdapter adapter = context .run(context -> {
.getBean(RequestMappingHandlerAdapter.class); RequestMappingHandlerAdapter adapter = context
List<HandlerMethodArgumentResolver> customResolvers = .getBean(RequestMappingHandlerAdapter.class);
(List<HandlerMethodArgumentResolver>) ReflectionTestUtils List<HandlerMethodArgumentResolver> customResolvers = (List<HandlerMethodArgumentResolver>) ReflectionTestUtils
.getField(adapter.getArgumentResolverConfigurer(), "customResolvers"); .getField(adapter.getArgumentResolverConfigurer(),
assertThat(customResolvers).contains( "customResolvers");
context.getBean("firstResolver", assertThat(customResolvers).contains(
HandlerMethodArgumentResolver.class), context.getBean("firstResolver",
context.getBean("secondResolver", HandlerMethodArgumentResolver.class),
HandlerMethodArgumentResolver.class)); context.getBean("secondResolver",
}); HandlerMethodArgumentResolver.class));
});
} }
@Test @Test
public void shouldCustomizeCodecs() { public void shouldCustomizeCodecs() {
this.contextRunner.withUserConfiguration(CustomCodecCustomizers.class).run(context -> { this.contextRunner.withUserConfiguration(CustomCodecCustomizers.class)
CodecCustomizer codecCustomizer = context.getBean("firstCodecCustomizer", .run(context -> {
CodecCustomizer.class); CodecCustomizer codecCustomizer = context
assertThat(codecCustomizer).isNotNull(); .getBean("firstCodecCustomizer", CodecCustomizer.class);
verify(codecCustomizer).customize(any(ServerCodecConfigurer.class)); assertThat(codecCustomizer).isNotNull();
}); verify(codecCustomizer).customize(any(ServerCodecConfigurer.class));
});
} }
@Test @Test
@ -129,7 +132,8 @@ public class WebFluxAutoConfigurationTests {
SimpleUrlHandlerMapping hm = context.getBean("resourceHandlerMapping", SimpleUrlHandlerMapping hm = context.getBean("resourceHandlerMapping",
SimpleUrlHandlerMapping.class); SimpleUrlHandlerMapping.class);
assertThat(hm.getUrlMap().get("/**")).isInstanceOf(ResourceWebHandler.class); assertThat(hm.getUrlMap().get("/**")).isInstanceOf(ResourceWebHandler.class);
ResourceWebHandler staticHandler = (ResourceWebHandler) hm.getUrlMap().get("/**"); ResourceWebHandler staticHandler = (ResourceWebHandler) hm.getUrlMap()
.get("/**");
assertThat(staticHandler.getLocations()).hasSize(4); assertThat(staticHandler.getLocations()).hasSize(4);
assertThat(hm.getUrlMap().get("/webjars/**")) assertThat(hm.getUrlMap().get("/webjars/**"))
.isInstanceOf(ResourceWebHandler.class); .isInstanceOf(ResourceWebHandler.class);
@ -143,7 +147,8 @@ public class WebFluxAutoConfigurationTests {
@Test @Test
public void shouldMapResourcesToCustomPath() { public void shouldMapResourcesToCustomPath() {
this.contextRunner.withPropertyValues("spring.webflux.static-path-pattern:/static/**") this.contextRunner
.withPropertyValues("spring.webflux.static-path-pattern:/static/**")
.run(context -> { .run(context -> {
SimpleUrlHandlerMapping hm = context.getBean("resourceHandlerMapping", SimpleUrlHandlerMapping hm = context.getBean("resourceHandlerMapping",
SimpleUrlHandlerMapping.class); SimpleUrlHandlerMapping.class);
@ -170,11 +175,16 @@ public class WebFluxAutoConfigurationTests {
.run(context -> { .run(context -> {
SimpleUrlHandlerMapping hm = context.getBean("resourceHandlerMapping", SimpleUrlHandlerMapping hm = context.getBean("resourceHandlerMapping",
SimpleUrlHandlerMapping.class); SimpleUrlHandlerMapping.class);
assertThat(hm.getUrlMap().get("/**")).isInstanceOf(ResourceWebHandler.class); assertThat(hm.getUrlMap().get("/**"))
ResourceWebHandler staticHandler = (ResourceWebHandler) hm.getUrlMap().get("/**"); .isInstanceOf(ResourceWebHandler.class);
assertThat(staticHandler.getResourceResolvers()).extractingResultOf("getClass") ResourceWebHandler staticHandler = (ResourceWebHandler) hm.getUrlMap()
.containsOnly(CachingResourceResolver.class, PathResourceResolver.class); .get("/**");
assertThat(staticHandler.getResourceTransformers()).extractingResultOf("getClass") assertThat(staticHandler.getResourceResolvers())
.extractingResultOf("getClass")
.containsOnly(CachingResourceResolver.class,
PathResourceResolver.class);
assertThat(staticHandler.getResourceTransformers())
.extractingResultOf("getClass")
.containsOnly(CachingResourceTransformer.class); .containsOnly(CachingResourceTransformer.class);
}); });
} }
@ -216,29 +226,35 @@ public class WebFluxAutoConfigurationTests {
@Test @Test
public void validatorWhenNoValidatorShouldUseDefault() { public void validatorWhenNoValidatorShouldUseDefault() {
this.contextRunner this.contextRunner.run(context -> {
.run(context -> { assertThat(context).doesNotHaveBean(ValidatorFactory.class);
assertThat(context).doesNotHaveBean(ValidatorFactory.class); assertThat(context).doesNotHaveBean(javax.validation.Validator.class);
assertThat(context).doesNotHaveBean(javax.validation.Validator.class); assertThat(context).getBeanNames(Validator.class)
assertThat(context).getBeanNames(Validator.class).containsExactly("webFluxValidator"); .containsExactly("webFluxValidator");
}); });
} }
@Test @Test
public void validatorWhenNoCustomizationShouldUseAutoConfigured() { public void validatorWhenNoCustomizationShouldUseAutoConfigured() {
this.contextRunner.withConfiguration( this.contextRunner
AutoConfigurations.of(ValidationAutoConfiguration.class)) .withConfiguration(
AutoConfigurations.of(ValidationAutoConfiguration.class))
.run(context -> { .run(context -> {
assertThat(context).getBeanNames(javax.validation.Validator.class) assertThat(context).getBeanNames(javax.validation.Validator.class)
.containsExactly("defaultValidator"); .containsExactly("defaultValidator");
assertThat(context).getBeanNames(Validator.class) assertThat(context).getBeanNames(Validator.class)
.containsExactlyInAnyOrder("defaultValidator", "webFluxValidator"); .containsExactlyInAnyOrder("defaultValidator",
Validator validator = context.getBean("webFluxValidator", Validator.class); "webFluxValidator");
Validator validator = context.getBean("webFluxValidator",
Validator.class);
assertThat(validator).isInstanceOf(ValidatorAdapter.class); assertThat(validator).isInstanceOf(ValidatorAdapter.class);
Object defaultValidator = context.getBean("defaultValidator"); Object defaultValidator = context.getBean("defaultValidator");
assertThat(((ValidatorAdapter) validator).getTarget()).isSameAs(defaultValidator); assertThat(((ValidatorAdapter) validator).getTarget())
// Primary Spring validator is the one used by WebFlux behind the scenes .isSameAs(defaultValidator);
assertThat(context.getBean(Validator.class)).isEqualTo(defaultValidator); // Primary Spring validator is the one used by WebFlux behind the
// scenes
assertThat(context.getBean(Validator.class))
.isEqualTo(defaultValidator);
}); });
} }
@ -250,15 +266,14 @@ public class WebFluxAutoConfigurationTests {
assertThat(context).doesNotHaveBean(javax.validation.Validator.class); assertThat(context).doesNotHaveBean(javax.validation.Validator.class);
assertThat(context).getBeanNames(Validator.class) assertThat(context).getBeanNames(Validator.class)
.containsOnly("webFluxValidator"); .containsOnly("webFluxValidator");
assertThat(context.getBean("webFluxValidator")) assertThat(context.getBean("webFluxValidator")).isSameAs(
.isSameAs(context.getBean(ValidatorWebFluxConfigurer.class).validator); context.getBean(ValidatorWebFluxConfigurer.class).validator);
}); });
} }
@Test @Test
public void validatorWithConfigurerDoesNotExposeJsr303() { public void validatorWithConfigurerDoesNotExposeJsr303() {
this.contextRunner this.contextRunner.withUserConfiguration(ValidatorJsr303WebFluxConfigurer.class)
.withUserConfiguration(ValidatorJsr303WebFluxConfigurer.class)
.run(context -> { .run(context -> {
assertThat(context).doesNotHaveBean(ValidatorFactory.class); assertThat(context).doesNotHaveBean(ValidatorFactory.class);
assertThat(context).doesNotHaveBean(javax.validation.Validator.class); assertThat(context).doesNotHaveBean(javax.validation.Validator.class);
@ -267,24 +282,28 @@ public class WebFluxAutoConfigurationTests {
Validator validator = context.getBean("webFluxValidator", Validator validator = context.getBean("webFluxValidator",
Validator.class); Validator.class);
assertThat(validator).isInstanceOf(ValidatorAdapter.class); assertThat(validator).isInstanceOf(ValidatorAdapter.class);
assertThat(((ValidatorAdapter) validator).getTarget()).isSameAs( assertThat(((ValidatorAdapter) validator).getTarget())
context.getBean(ValidatorJsr303WebFluxConfigurer.class).validator); .isSameAs(context.getBean(
ValidatorJsr303WebFluxConfigurer.class).validator);
}); });
} }
@Test @Test
public void validationCustomConfigurerTakesPrecedence() { public void validationCustomConfigurerTakesPrecedence() {
this.contextRunner this.contextRunner
.withConfiguration(AutoConfigurations.of(ValidationAutoConfiguration.class)) .withConfiguration(
.withUserConfiguration(ValidatorWebFluxConfigurer.class) AutoConfigurations.of(ValidationAutoConfiguration.class))
.run(context -> { .withUserConfiguration(ValidatorWebFluxConfigurer.class).run(context -> {
assertThat(context).getBeans(ValidatorFactory.class).hasSize(1); assertThat(context).getBeans(ValidatorFactory.class).hasSize(1);
assertThat(context).getBeans(javax.validation.Validator.class).hasSize(1); assertThat(context).getBeans(javax.validation.Validator.class)
.hasSize(1);
assertThat(context).getBeanNames(Validator.class) assertThat(context).getBeanNames(Validator.class)
.containsExactlyInAnyOrder("defaultValidator", "webFluxValidator"); .containsExactlyInAnyOrder("defaultValidator",
"webFluxValidator");
assertThat(context.getBean("webFluxValidator")).isSameAs( assertThat(context.getBean("webFluxValidator")).isSameAs(
context.getBean(ValidatorWebFluxConfigurer.class).validator); context.getBean(ValidatorWebFluxConfigurer.class).validator);
// Primary Spring validator is the auto-configured one as the WebFlux one has been // Primary Spring validator is the auto-configured one as the WebFlux
// one has been
// customized via a WebFluxConfigurer // customized via a WebFluxConfigurer
assertThat(context.getBean(Validator.class)) assertThat(context.getBean(Validator.class))
.isEqualTo(context.getBean("defaultValidator")); .isEqualTo(context.getBean("defaultValidator"));
@ -294,19 +313,24 @@ public class WebFluxAutoConfigurationTests {
@Test @Test
public void validatorWithCustomSpringValidatorIgnored() { public void validatorWithCustomSpringValidatorIgnored() {
this.contextRunner this.contextRunner
.withConfiguration(AutoConfigurations.of(ValidationAutoConfiguration.class)) .withConfiguration(
.withUserConfiguration(CustomSpringValidator.class) AutoConfigurations.of(ValidationAutoConfiguration.class))
.run(context -> { .withUserConfiguration(CustomSpringValidator.class).run(context -> {
assertThat(context).getBeanNames(javax.validation.Validator.class) assertThat(context).getBeanNames(javax.validation.Validator.class)
.containsExactly("defaultValidator"); .containsExactly("defaultValidator");
assertThat(context).getBeanNames(Validator.class) assertThat(context).getBeanNames(Validator.class)
.containsExactlyInAnyOrder("customValidator", "defaultValidator", "webFluxValidator"); .containsExactlyInAnyOrder("customValidator",
Validator validator = context.getBean("webFluxValidator", Validator.class); "defaultValidator", "webFluxValidator");
Validator validator = context.getBean("webFluxValidator",
Validator.class);
assertThat(validator).isInstanceOf(ValidatorAdapter.class); assertThat(validator).isInstanceOf(ValidatorAdapter.class);
Object defaultValidator = context.getBean("defaultValidator"); Object defaultValidator = context.getBean("defaultValidator");
assertThat(((ValidatorAdapter) validator).getTarget()).isSameAs(defaultValidator); assertThat(((ValidatorAdapter) validator).getTarget())
// Primary Spring validator is the one used by WebFlux behind the scenes .isSameAs(defaultValidator);
assertThat(context.getBean(Validator.class)).isEqualTo(defaultValidator); // Primary Spring validator is the one used by WebFlux behind the
// scenes
assertThat(context.getBean(Validator.class))
.isEqualTo(defaultValidator);
}); });
} }
@ -322,8 +346,9 @@ public class WebFluxAutoConfigurationTests {
Validator validator = context.getBean(Validator.class); Validator validator = context.getBean(Validator.class);
assertThat(validator).isInstanceOf(ValidatorAdapter.class); assertThat(validator).isInstanceOf(ValidatorAdapter.class);
Validator target = ((ValidatorAdapter) validator).getTarget(); Validator target = ((ValidatorAdapter) validator).getTarget();
assertThat(new DirectFieldAccessor(target).getPropertyValue("targetValidator")) assertThat(new DirectFieldAccessor(target)
.isSameAs(context.getBean("customValidator")); .getPropertyValue("targetValidator"))
.isSameAs(context.getBean("customValidator"));
}); });
} }