Merge branch '5.3.x'

This commit is contained in:
rstoyanchev 2022-07-13 19:21:33 +01:00
commit f814fb420b
8 changed files with 82 additions and 37 deletions

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2021 the original author or authors. * Copyright 2002-2022 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -705,8 +705,8 @@ public class AntPathMatcher implements PathMatcher {
else { else {
this.exactMatch = false; this.exactMatch = false;
patternBuilder.append(quote(pattern, end, pattern.length())); patternBuilder.append(quote(pattern, end, pattern.length()));
this.pattern = (this.caseSensitive ? Pattern.compile(patternBuilder.toString()) : this.pattern = Pattern.compile(patternBuilder.toString(),
Pattern.compile(patternBuilder.toString(), Pattern.CASE_INSENSITIVE)); Pattern.DOTALL | (this.caseSensitive ? 0 : Pattern.CASE_INSENSITIVE));
} }
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2020 the original author or authors. * Copyright 2002-2022 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -131,6 +131,7 @@ class AntPathMatcherTests {
assertThat(pathMatcher.match("/{bla}.*", "/testing.html")).isTrue(); assertThat(pathMatcher.match("/{bla}.*", "/testing.html")).isTrue();
assertThat(pathMatcher.match("/{bla}", "//x\ny")).isTrue(); assertThat(pathMatcher.match("/{bla}", "//x\ny")).isTrue();
assertThat(pathMatcher.match("/{var:.*}", "/x\ny")).isTrue();
} }
@Test @Test

View File

@ -520,7 +520,7 @@ public class Jackson2ObjectMapperBuilder {
} }
/** /**
* Specify one or more modules to be registered with the {@link ObjectMapper}. * Specify the modules to be registered with the {@link ObjectMapper}.
* <p>Multiple invocations are not additive, the last one defines the modules to * <p>Multiple invocations are not additive, the last one defines the modules to
* register. * register.
* <p>Note: If this is set, no finding of modules is going to happen - not by * <p>Note: If this is set, no finding of modules is going to happen - not by
@ -537,15 +537,9 @@ public class Jackson2ObjectMapperBuilder {
} }
/** /**
* Set a complete list of modules to be registered with the {@link ObjectMapper}. * Variant of {@link #modules(Module...)} with a {@link List}.
* <p>Multiple invocations are not additive, the last one defines the modules to
* register.
* <p>Note: If this is set, no finding of modules is going to happen - not by
* Jackson, and not by Spring either (see {@link #findModulesViaServiceLoader}).
* As a consequence, specifying an empty list here will suppress any kind of
* module detection.
* <p>Specify either this or {@link #modulesToInstall}, not both.
* @see #modules(Module...) * @see #modules(Module...)
* @see #modules(Consumer)
* @see com.fasterxml.jackson.databind.Module * @see com.fasterxml.jackson.databind.Module
*/ */
public Jackson2ObjectMapperBuilder modules(List<Module> modules) { public Jackson2ObjectMapperBuilder modules(List<Module> modules) {
@ -555,6 +549,22 @@ public class Jackson2ObjectMapperBuilder {
return this; return this;
} }
/**
* Variant of {@link #modules(Module...)} with a {@link Consumer} for full
* control over the underlying list of modules.
* @since 6.0
* @see #modules(Module...)
* @see #modules(List)
* @see com.fasterxml.jackson.databind.Module
*/
public Jackson2ObjectMapperBuilder modules(Consumer<List<Module>> consumer) {
this.modules = (this.modules != null ? this.modules : new ArrayList<>());
this.findModulesViaServiceLoader = false;
this.findWellKnownModules = false;
consumer.accept(this.modules);
return this;
}
/** /**
* Specify one or more modules to be registered with the {@link ObjectMapper}. * Specify one or more modules to be registered with the {@link ObjectMapper}.
* <p>Multiple invocations are not additive, the last one defines the modules * <p>Multiple invocations are not additive, the last one defines the modules
@ -565,6 +575,7 @@ public class Jackson2ObjectMapperBuilder {
* allowing to eventually override their configuration. * allowing to eventually override their configuration.
* <p>Specify either this or {@link #modules(Module...)}, not both. * <p>Specify either this or {@link #modules(Module...)}, not both.
* @since 4.1.5 * @since 4.1.5
* @see #modulesToInstall(Consumer)
* @see #modulesToInstall(Class...) * @see #modulesToInstall(Class...)
* @see com.fasterxml.jackson.databind.Module * @see com.fasterxml.jackson.databind.Module
*/ */
@ -574,6 +585,21 @@ public class Jackson2ObjectMapperBuilder {
return this; return this;
} }
/**
* Variant of {@link #modulesToInstall(Module...)} with a {@link Consumer}
* for full control over the underlying list of modules.
* @since 6.0
* @see #modulesToInstall(Module...)
* @see #modulesToInstall(Class...)
* @see com.fasterxml.jackson.databind.Module
*/
public Jackson2ObjectMapperBuilder modulesToInstall(Consumer<List<Module>> consumer) {
this.modules = (this.modules != null ? this.modules : new ArrayList<>());
this.findWellKnownModules = true;
consumer.accept(this.modules);
return this;
}
/** /**
* Specify one or more modules by class to be registered with * Specify one or more modules by class to be registered with
* the {@link ObjectMapper}. * the {@link ObjectMapper}.
@ -585,6 +611,7 @@ public class Jackson2ObjectMapperBuilder {
* allowing to eventually override their configuration. * allowing to eventually override their configuration.
* <p>Specify either this or {@link #modules(Module...)}, not both. * <p>Specify either this or {@link #modules(Module...)}, not both.
* @see #modulesToInstall(Module...) * @see #modulesToInstall(Module...)
* @see #modulesToInstall(Consumer)
* @see com.fasterxml.jackson.databind.Module * @see com.fasterxml.jackson.databind.Module
*/ */
@SafeVarargs @SafeVarargs

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2021 the original author or authors. * Copyright 2002-2022 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -59,15 +59,9 @@ class CaptureVariablePathElement extends PathElement {
} }
else { else {
this.variableName = new String(captureDescriptor, 1, colon - 1); this.variableName = new String(captureDescriptor, 1, colon - 1);
if (caseSensitive) { this.constraintPattern = Pattern.compile(
this.constraintPattern = Pattern.compile( new String(captureDescriptor, colon + 1, captureDescriptor.length - colon - 2),
new String(captureDescriptor, colon + 1, captureDescriptor.length - colon - 2)); Pattern.DOTALL | (caseSensitive ? 0 : Pattern.CASE_INSENSITIVE));
}
else {
this.constraintPattern = Pattern.compile(
new String(captureDescriptor, colon + 1, captureDescriptor.length - colon - 2),
Pattern.CASE_INSENSITIVE);
}
} }
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2021 the original author or authors. * Copyright 2002-2022 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -108,12 +108,8 @@ class RegexPathElement extends PathElement {
} }
patternBuilder.append(quote(text, end, text.length())); patternBuilder.append(quote(text, end, text.length()));
if (this.caseSensitive) { return Pattern.compile(patternBuilder.toString(),
return Pattern.compile(patternBuilder.toString()); Pattern.DOTALL | (this.caseSensitive ? 0 : Pattern.CASE_INSENSITIVE));
}
else {
return Pattern.compile(patternBuilder.toString(), Pattern.CASE_INSENSITIVE);
}
} }
public List<String> getVariableNames() { public List<String> getVariableNames() {

View File

@ -240,6 +240,16 @@ class Jackson2ObjectMapperBuilderTests {
assertThat(serializers.findSerializer(null, SimpleType.construct(Integer.class), null)).isSameAs(serializer1); assertThat(serializers.findSerializer(null, SimpleType.construct(Integer.class), null)).isSameAs(serializer1);
} }
@Test
void modulesWithConsumer() {
NumberSerializer serializer1 = new NumberSerializer(Integer.class);
SimpleModule module = new SimpleModule();
module.addSerializer(Integer.class, serializer1);
ObjectMapper objectMapper = Jackson2ObjectMapperBuilder.json().modules(list -> list.add(module) ).build();
Serializers serializers = getSerializerFactoryConfig(objectMapper).serializers().iterator().next();
assertThat(serializers.findSerializer(null, SimpleType.construct(Integer.class), null)).isSameAs(serializer1);
}
@Test @Test
void modulesToInstallByClass() { void modulesToInstallByClass() {
ObjectMapper objectMapper = Jackson2ObjectMapperBuilder.json() ObjectMapper objectMapper = Jackson2ObjectMapperBuilder.json()
@ -258,6 +268,15 @@ class Jackson2ObjectMapperBuilderTests {
assertThat(serializers.findSerializer(null, SimpleType.construct(Integer.class), null).getClass()).isSameAs(CustomIntegerSerializer.class); assertThat(serializers.findSerializer(null, SimpleType.construct(Integer.class), null).getClass()).isSameAs(CustomIntegerSerializer.class);
} }
@Test
void modulesToInstallWithConsumer() {
ObjectMapper objectMapper = Jackson2ObjectMapperBuilder.json()
.modulesToInstall(list -> list.add(new CustomIntegerModule()))
.build();
Serializers serializers = getSerializerFactoryConfig(objectMapper).serializers().iterator().next();
assertThat(serializers.findSerializer(null, SimpleType.construct(Integer.class), null).getClass()).isSameAs(CustomIntegerSerializer.class);
}
@Test @Test
void wellKnownModules() throws JsonProcessingException, UnsupportedEncodingException { void wellKnownModules() throws JsonProcessingException, UnsupportedEncodingException {
ObjectMapper objectMapper = Jackson2ObjectMapperBuilder.json().build(); ObjectMapper objectMapper = Jackson2ObjectMapperBuilder.json().build();

View File

@ -301,6 +301,7 @@ public class PathPatternTests {
checkCapture("{var:f o}","f%20o","var","f o"); // constraint is expressed in non encoded form checkCapture("{var:f o}","f%20o","var","f o"); // constraint is expressed in non encoded form
checkCapture("{var:f.o}","f%20o","var","f o"); checkCapture("{var:f.o}","f%20o","var","f o");
checkCapture("{var:f\\|o}","f%7co","var","f|o"); checkCapture("{var:f\\|o}","f%7co","var","f|o");
checkCapture("{var:.*}","x\ny","var","x\ny");
} }
@Test @Test
@ -320,6 +321,8 @@ public class PathPatternTests {
checkCapture("/{var1}_ _{var2}","/f%20o_%20_f%7co","var1","f o","var2","f|o"); checkCapture("/{var1}_ _{var2}","/f%20o_%20_f%7co","var1","f o","var2","f|o");
checkCapture("/{var1}_ _{var2:f\\|o}","/f%20o_%20_f%7co","var1","f o","var2","f|o"); checkCapture("/{var1}_ _{var2:f\\|o}","/f%20o_%20_f%7co","var1","f o","var2","f|o");
checkCapture("/{var1:f o}_ _{var2:f\\|o}","/f%20o_%20_f%7co","var1","f o","var2","f|o"); checkCapture("/{var1:f o}_ _{var2:f\\|o}","/f%20o_%20_f%7co","var1","f o","var2","f|o");
checkCapture("/{var1:f o}_ _{var2:f\\|o}","/f%20o_%20_f%7co","var1","f o","var2","f|o");
checkCapture("/{var1}_{var2}","/f\noo_foo","var1","f\noo","var2","foo");
} }
@Test @Test

View File

@ -650,7 +650,7 @@ See <<mvc-config-interceptors>> in the section on MVC configuration for examples
configure interceptors. You can also register them directly by using setters on individual configure interceptors. You can also register them directly by using setters on individual
`HandlerMapping` implementations. `HandlerMapping` implementations.
Note that `postHandle` is less useful with `@ResponseBody` and `ResponseEntity` methods for `postHandle` method is less useful with `@ResponseBody` and `ResponseEntity` methods for
which the response is written and committed within the `HandlerAdapter` and before which the response is written and committed within the `HandlerAdapter` and before
`postHandle`. That means it is too late to make any changes to the response, such as adding `postHandle`. That means it is too late to make any changes to the response, such as adding
an extra header. For such scenarios, you can implement `ResponseBodyAdvice` and either an extra header. For such scenarios, you can implement `ResponseBodyAdvice` and either
@ -659,6 +659,7 @@ declare it as an <<mvc-ann-controller-advice>> bean or configure it directly on
[[mvc-exceptionhandlers]] [[mvc-exceptionhandlers]]
=== Exceptions === Exceptions
[.small]#<<web-reactive.adoc#webflux-dispatcher-exceptions, WebFlux>># [.small]#<<web-reactive.adoc#webflux-dispatcher-exceptions, WebFlux>>#
@ -5397,7 +5398,6 @@ the following example shows:
public void addInterceptors(InterceptorRegistry registry) { public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LocaleChangeInterceptor()); registry.addInterceptor(new LocaleChangeInterceptor());
registry.addInterceptor(new ThemeChangeInterceptor()).addPathPatterns("/**").excludePathPatterns("/admin/**"); registry.addInterceptor(new ThemeChangeInterceptor()).addPathPatterns("/**").excludePathPatterns("/admin/**");
registry.addInterceptor(new SecurityInterceptor()).addPathPatterns("/secure/*");
} }
} }
---- ----
@ -5411,7 +5411,6 @@ the following example shows:
override fun addInterceptors(registry: InterceptorRegistry) { override fun addInterceptors(registry: InterceptorRegistry) {
registry.addInterceptor(LocaleChangeInterceptor()) registry.addInterceptor(LocaleChangeInterceptor())
registry.addInterceptor(ThemeChangeInterceptor()).addPathPatterns("/**").excludePathPatterns("/admin/**") registry.addInterceptor(ThemeChangeInterceptor()).addPathPatterns("/**").excludePathPatterns("/admin/**")
registry.addInterceptor(SecurityInterceptor()).addPathPatterns("/secure/*")
} }
} }
---- ----
@ -5427,13 +5426,19 @@ The following example shows how to achieve the same configuration in XML:
<mvc:exclude-mapping path="/admin/**"/> <mvc:exclude-mapping path="/admin/**"/>
<bean class="org.springframework.web.servlet.theme.ThemeChangeInterceptor"/> <bean class="org.springframework.web.servlet.theme.ThemeChangeInterceptor"/>
</mvc:interceptor> </mvc:interceptor>
<mvc:interceptor>
<mvc:mapping path="/secure/*"/>
<bean class="org.example.SecurityInterceptor"/>
</mvc:interceptor>
</mvc:interceptors> </mvc:interceptors>
---- ----
NOTE: Mapped interceptors are not ideally suited as a security layer due to the potential
for a mismatch with annotated controller path matching, which can also match trailing
slashes and path extensions transparently, along with other path matching options. Many
of these options have been deprecated but the potential for a mismatch remains.
Generally, we recommend using Spring Security which includes a dedicated
https://docs.spring.io/spring-security/reference/servlet/integrations/mvc.html#mvc-requestmatcher[MvcRequestMatcher]
to align with Spring MVC path matching and also has a security firewall that blocks many
unwanted characters in URL paths.
[[mvc-config-content-negotiation]] [[mvc-config-content-negotiation]]