Skip to content

Commit 26fecbe

Browse files
committed
Upgrade to Thymeleaf and Security Extras 3.1.0-M1
Closes gh-49452 Closes gh-49453
1 parent 12cd97a commit 26fecbe

File tree

13 files changed

+140
-105
lines changed

13 files changed

+140
-105
lines changed

spring-boot-project/spring-boot-autoconfigure/build.gradle

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -178,9 +178,9 @@ dependencies {
178178
exclude group: "jakarta.mail", module: "jakarta.mail-api"
179179
}
180180
optional("org.thymeleaf:thymeleaf")
181-
optional("org.thymeleaf:thymeleaf-spring5")
181+
optional("org.thymeleaf:thymeleaf-spring6")
182182
optional("org.thymeleaf.extras:thymeleaf-extras-java8time")
183-
optional("org.thymeleaf.extras:thymeleaf-extras-springsecurity5")
183+
optional("org.thymeleaf.extras:thymeleaf-extras-springsecurity6")
184184
optional("redis.clients:jedis")
185185

186186
testImplementation(project(":spring-boot-project:spring-boot-tools:spring-boot-test-support"))
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
/*
2+
* Copyright 2012-2022 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.autoconfigure.thymeleaf;
18+
19+
import org.thymeleaf.ITemplateEngine;
20+
import org.thymeleaf.dialect.IDialect;
21+
import org.thymeleaf.spring6.ISpringTemplateEngine;
22+
import org.thymeleaf.spring6.ISpringWebFluxTemplateEngine;
23+
import org.thymeleaf.spring6.SpringTemplateEngine;
24+
import org.thymeleaf.spring6.SpringWebFluxTemplateEngine;
25+
import org.thymeleaf.templateresolver.ITemplateResolver;
26+
27+
import org.springframework.beans.factory.ObjectProvider;
28+
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
29+
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
30+
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
31+
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication.Type;
32+
import org.springframework.context.annotation.Bean;
33+
import org.springframework.context.annotation.Configuration;
34+
35+
/**
36+
* Configuration classes for Thymeleaf's {@link ITemplateEngine}. Imported by
37+
* {@link ThymeleafAutoConfiguration}.
38+
*
39+
* @author Andy Wilkinson
40+
*/
41+
class TemplateEngineConfigurations {
42+
43+
@Configuration(proxyBeanMethods = false)
44+
static class DefaultTemplateEngineConfiguration {
45+
46+
@Bean
47+
@ConditionalOnMissingBean(ISpringTemplateEngine.class)
48+
SpringTemplateEngine templateEngine(ThymeleafProperties properties,
49+
ObjectProvider<ITemplateResolver> templateResolvers, ObjectProvider<IDialect> dialects) {
50+
SpringTemplateEngine engine = new SpringTemplateEngine();
51+
engine.setEnableSpringELCompiler(properties.isEnableSpringElCompiler());
52+
engine.setRenderHiddenMarkersBeforeCheckboxes(properties.isRenderHiddenMarkersBeforeCheckboxes());
53+
templateResolvers.orderedStream().forEach(engine::addTemplateResolver);
54+
dialects.orderedStream().forEach(engine::addDialect);
55+
return engine;
56+
}
57+
58+
}
59+
60+
@Configuration(proxyBeanMethods = false)
61+
@ConditionalOnWebApplication(type = Type.REACTIVE)
62+
@ConditionalOnProperty(name = "spring.thymeleaf.enabled", matchIfMissing = true)
63+
static class ReactiveTemplateEngineConfiguration {
64+
65+
@Bean
66+
@ConditionalOnMissingBean(ISpringWebFluxTemplateEngine.class)
67+
SpringWebFluxTemplateEngine templateEngine(ThymeleafProperties properties,
68+
ObjectProvider<ITemplateResolver> templateResolvers, ObjectProvider<IDialect> dialects) {
69+
SpringWebFluxTemplateEngine engine = new SpringWebFluxTemplateEngine();
70+
engine.setEnableSpringELCompiler(properties.isEnableSpringElCompiler());
71+
engine.setRenderHiddenMarkersBeforeCheckboxes(properties.isRenderHiddenMarkersBeforeCheckboxes());
72+
templateResolvers.orderedStream().forEach(engine::addTemplateResolver);
73+
dialects.orderedStream().forEach(engine::addDialect);
74+
return engine;
75+
}
76+
77+
}
78+
79+
}

spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/thymeleaf/ThymeleafAutoConfiguration.java

Lines changed: 11 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2021 the original author or authors.
2+
* Copyright 2012-2022 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -18,26 +18,21 @@
1818

1919
import java.util.LinkedHashMap;
2020

21-
import javax.servlet.DispatcherType;
21+
import jakarta.servlet.DispatcherType;
2222

2323
import com.github.mxab.thymeleaf.extras.dataattribute.dialect.DataAttributeDialect;
2424
import nz.net.ultraq.thymeleaf.layoutdialect.LayoutDialect;
2525
import org.apache.commons.logging.Log;
2626
import org.apache.commons.logging.LogFactory;
27-
import org.thymeleaf.dialect.IDialect;
2827
import org.thymeleaf.extras.java8time.dialect.Java8TimeDialect;
29-
import org.thymeleaf.extras.springsecurity5.dialect.SpringSecurityDialect;
30-
import org.thymeleaf.spring5.ISpringTemplateEngine;
31-
import org.thymeleaf.spring5.ISpringWebFluxTemplateEngine;
32-
import org.thymeleaf.spring5.SpringTemplateEngine;
33-
import org.thymeleaf.spring5.SpringWebFluxTemplateEngine;
34-
import org.thymeleaf.spring5.templateresolver.SpringResourceTemplateResolver;
35-
import org.thymeleaf.spring5.view.ThymeleafViewResolver;
36-
import org.thymeleaf.spring5.view.reactive.ThymeleafReactiveViewResolver;
28+
import org.thymeleaf.extras.springsecurity6.dialect.SpringSecurityDialect;
29+
import org.thymeleaf.spring6.ISpringWebFluxTemplateEngine;
30+
import org.thymeleaf.spring6.SpringTemplateEngine;
31+
import org.thymeleaf.spring6.templateresolver.SpringResourceTemplateResolver;
32+
import org.thymeleaf.spring6.view.ThymeleafViewResolver;
33+
import org.thymeleaf.spring6.view.reactive.ThymeleafReactiveViewResolver;
3734
import org.thymeleaf.templatemode.TemplateMode;
38-
import org.thymeleaf.templateresolver.ITemplateResolver;
3935

40-
import org.springframework.beans.factory.ObjectProvider;
4136
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
4237
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
4338
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
@@ -57,6 +52,7 @@
5752
import org.springframework.context.ApplicationContext;
5853
import org.springframework.context.annotation.Bean;
5954
import org.springframework.context.annotation.Configuration;
55+
import org.springframework.context.annotation.Import;
6056
import org.springframework.core.Ordered;
6157
import org.springframework.util.MimeType;
6258
import org.springframework.util.unit.DataSize;
@@ -79,6 +75,8 @@
7975
@EnableConfigurationProperties(ThymeleafProperties.class)
8076
@ConditionalOnClass({ TemplateMode.class, SpringTemplateEngine.class })
8177
@AutoConfigureAfter({ WebMvcAutoConfiguration.class, WebFluxAutoConfiguration.class })
78+
@Import({ TemplateEngineConfigurations.ReactiveTemplateEngineConfiguration.class,
79+
TemplateEngineConfigurations.DefaultTemplateEngineConfiguration.class })
8280
public class ThymeleafAutoConfiguration {
8381

8482
@Configuration(proxyBeanMethods = false)
@@ -129,23 +127,6 @@ SpringResourceTemplateResolver defaultTemplateResolver() {
129127

130128
}
131129

132-
@Configuration(proxyBeanMethods = false)
133-
protected static class ThymeleafDefaultConfiguration {
134-
135-
@Bean
136-
@ConditionalOnMissingBean(ISpringTemplateEngine.class)
137-
SpringTemplateEngine templateEngine(ThymeleafProperties properties,
138-
ObjectProvider<ITemplateResolver> templateResolvers, ObjectProvider<IDialect> dialects) {
139-
SpringTemplateEngine engine = new SpringTemplateEngine();
140-
engine.setEnableSpringELCompiler(properties.isEnableSpringElCompiler());
141-
engine.setRenderHiddenMarkersBeforeCheckboxes(properties.isRenderHiddenMarkersBeforeCheckboxes());
142-
templateResolvers.orderedStream().forEach(engine::addTemplateResolver);
143-
dialects.orderedStream().forEach(engine::addDialect);
144-
return engine;
145-
}
146-
147-
}
148-
149130
@Configuration(proxyBeanMethods = false)
150131
@ConditionalOnWebApplication(type = Type.SERVLET)
151132
@ConditionalOnProperty(name = "spring.thymeleaf.enabled", matchIfMissing = true)
@@ -198,25 +179,6 @@ private String appendCharset(MimeType type, String charset) {
198179

199180
}
200181

201-
@Configuration(proxyBeanMethods = false)
202-
@ConditionalOnWebApplication(type = Type.REACTIVE)
203-
@ConditionalOnProperty(name = "spring.thymeleaf.enabled", matchIfMissing = true)
204-
static class ThymeleafReactiveConfiguration {
205-
206-
@Bean
207-
@ConditionalOnMissingBean(ISpringWebFluxTemplateEngine.class)
208-
SpringWebFluxTemplateEngine templateEngine(ThymeleafProperties properties,
209-
ObjectProvider<ITemplateResolver> templateResolvers, ObjectProvider<IDialect> dialects) {
210-
SpringWebFluxTemplateEngine engine = new SpringWebFluxTemplateEngine();
211-
engine.setEnableSpringELCompiler(properties.isEnableSpringElCompiler());
212-
engine.setRenderHiddenMarkersBeforeCheckboxes(properties.isRenderHiddenMarkersBeforeCheckboxes());
213-
templateResolvers.orderedStream().forEach(engine::addTemplateResolver);
214-
dialects.orderedStream().forEach(engine::addDialect);
215-
return engine;
216-
}
217-
218-
}
219-
220182
@Configuration(proxyBeanMethods = false)
221183
@ConditionalOnWebApplication(type = Type.REACTIVE)
222184
@ConditionalOnProperty(name = "spring.thymeleaf.enabled", matchIfMissing = true)

spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/thymeleaf/ThymeleafTemplateAvailabilityProvider.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2019 the original author or authors.
2+
* Copyright 2012-2022 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -34,7 +34,7 @@ public class ThymeleafTemplateAvailabilityProvider implements TemplateAvailabili
3434
@Override
3535
public boolean isTemplateAvailable(String view, Environment environment, ClassLoader classLoader,
3636
ResourceLoader resourceLoader) {
37-
if (ClassUtils.isPresent("org.thymeleaf.spring5.SpringTemplateEngine", classLoader)) {
37+
if (ClassUtils.isPresent("org.thymeleaf.spring6.SpringTemplateEngine", classLoader)) {
3838
String prefix = environment.getProperty("spring.thymeleaf.prefix", ThymeleafProperties.DEFAULT_PREFIX);
3939
String suffix = environment.getProperty("spring.thymeleaf.suffix", ThymeleafProperties.DEFAULT_SUFFIX);
4040
return resourceLoader.getResource(prefix + view + suffix).exists();

spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/thymeleaf/ThymeleafReactiveAutoConfigurationTests.java

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2021 the original author or authors.
2+
* Copyright 2012-2022 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -17,6 +17,7 @@
1717
package org.springframework.boot.autoconfigure.thymeleaf;
1818

1919
import java.io.File;
20+
import java.nio.charset.StandardCharsets;
2021
import java.util.Collections;
2122
import java.util.Locale;
2223

@@ -26,13 +27,13 @@
2627
import org.junit.jupiter.api.extension.ExtendWith;
2728
import org.thymeleaf.TemplateEngine;
2829
import org.thymeleaf.context.Context;
29-
import org.thymeleaf.context.IContext;
30-
import org.thymeleaf.extras.springsecurity5.util.SpringSecurityContextUtils;
31-
import org.thymeleaf.spring5.ISpringWebFluxTemplateEngine;
32-
import org.thymeleaf.spring5.SpringWebFluxTemplateEngine;
33-
import org.thymeleaf.spring5.context.webflux.SpringWebFluxContext;
34-
import org.thymeleaf.spring5.templateresolver.SpringResourceTemplateResolver;
35-
import org.thymeleaf.spring5.view.reactive.ThymeleafReactiveViewResolver;
30+
import org.thymeleaf.context.WebContext;
31+
import org.thymeleaf.extras.springsecurity6.util.SpringSecurityContextUtils;
32+
import org.thymeleaf.spring6.ISpringWebFluxTemplateEngine;
33+
import org.thymeleaf.spring6.SpringWebFluxTemplateEngine;
34+
import org.thymeleaf.spring6.templateresolver.SpringResourceTemplateResolver;
35+
import org.thymeleaf.spring6.view.reactive.ThymeleafReactiveViewResolver;
36+
import org.thymeleaf.spring6.web.webflux.SpringWebFluxWebApplication;
3637
import org.thymeleaf.templateresolver.ITemplateResolver;
3738

3839
import org.springframework.boot.autoconfigure.AutoConfigurations;
@@ -198,7 +199,8 @@ void useSecurityDialect() {
198199
MockServerWebExchange exchange = MockServerWebExchange.from(MockServerHttpRequest.get("/test").build());
199200
exchange.getAttributes().put(SpringSecurityContextUtils.SECURITY_CONTEXT_MODEL_ATTRIBUTE_NAME,
200201
new SecurityContextImpl(new TestingAuthenticationToken("alice", "admin")));
201-
IContext attrs = new SpringWebFluxContext(exchange);
202+
WebContext attrs = new WebContext(SpringWebFluxWebApplication.buildApplication(null).buildExchange(exchange,
203+
Locale.US, MediaType.TEXT_HTML, StandardCharsets.UTF_8));
202204
String result = engine.process("security-dialect", attrs);
203205
assertThat(result).isEqualTo("<html><body><div>alice</div></body></html>" + System.lineSeparator());
204206
});

spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/thymeleaf/ThymeleafServletAutoConfigurationTests.java

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2021 the original author or authors.
2+
* Copyright 2012-2022 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -22,7 +22,8 @@
2222
import java.util.Locale;
2323
import java.util.Map;
2424

25-
import javax.servlet.DispatcherType;
25+
import jakarta.servlet.DispatcherType;
26+
import jakarta.servlet.ServletContext;
2627

2728
import nz.net.ultraq.thymeleaf.layoutdialect.LayoutDialect;
2829
import nz.net.ultraq.thymeleaf.layoutdialect.decorators.strategies.GroupingStrategy;
@@ -31,11 +32,12 @@
3132
import org.thymeleaf.TemplateEngine;
3233
import org.thymeleaf.context.Context;
3334
import org.thymeleaf.context.WebContext;
34-
import org.thymeleaf.spring5.SpringTemplateEngine;
35-
import org.thymeleaf.spring5.templateresolver.SpringResourceTemplateResolver;
36-
import org.thymeleaf.spring5.view.ThymeleafView;
37-
import org.thymeleaf.spring5.view.ThymeleafViewResolver;
35+
import org.thymeleaf.spring6.SpringTemplateEngine;
36+
import org.thymeleaf.spring6.templateresolver.SpringResourceTemplateResolver;
37+
import org.thymeleaf.spring6.view.ThymeleafView;
38+
import org.thymeleaf.spring6.view.ThymeleafViewResolver;
3839
import org.thymeleaf.templateresolver.ITemplateResolver;
40+
import org.thymeleaf.web.servlet.JakartaServletWebApplication;
3941

4042
import org.springframework.boot.autoconfigure.AutoConfigurations;
4143
import org.springframework.boot.test.context.FilteredClassLoader;
@@ -81,7 +83,7 @@ class ThymeleafServletAutoConfigurationTests {
8183

8284
@Test
8385
void autoConfigurationBackOffWithoutThymeleafSpring() {
84-
this.contextRunner.withClassLoader(new FilteredClassLoader("org.thymeleaf.spring5"))
86+
this.contextRunner.withClassLoader(new FilteredClassLoader("org.thymeleaf.spring6"))
8587
.run((context) -> assertThat(context).doesNotHaveBean(TemplateEngine.class));
8688
}
8789

@@ -183,7 +185,7 @@ void createLayoutFromConfigClass() {
183185
ThymeleafView view = (ThymeleafView) context.getBean(ThymeleafViewResolver.class).resolveViewName("view",
184186
Locale.UK);
185187
MockHttpServletResponse response = new MockHttpServletResponse();
186-
MockHttpServletRequest request = new MockHttpServletRequest();
188+
MockHttpServletRequest request = new MockHttpServletRequest(context.getBean(ServletContext.class));
187189
request.setAttribute(RequestContext.WEB_APPLICATION_CONTEXT_ATTRIBUTE, context);
188190
view.render(Collections.singletonMap("foo", "bar"), request, response);
189191
String result = response.getContentAsString();
@@ -217,8 +219,10 @@ void useJava8TimeDialect() {
217219
void useSecurityDialect() {
218220
this.contextRunner.run((context) -> {
219221
TemplateEngine engine = context.getBean(TemplateEngine.class);
220-
WebContext attrs = new WebContext(new MockHttpServletRequest(), new MockHttpServletResponse(),
221-
new MockServletContext());
222+
MockServletContext servletContext = new MockServletContext();
223+
JakartaServletWebApplication webApplication = JakartaServletWebApplication.buildApplication(servletContext);
224+
WebContext attrs = new WebContext(webApplication.buildExchange(new MockHttpServletRequest(servletContext),
225+
new MockHttpServletResponse()));
222226
try {
223227
SecurityContextHolder
224228
.setContext(new SecurityContextImpl(new TestingAuthenticationToken("alice", "admin")));
Lines changed: 1 addition & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
package app
22

3-
@Grab("thymeleaf-spring5")
3+
@Grab("thymeleaf-spring6")
44
@Controller
55
class Example {
66

@@ -10,24 +10,3 @@ class Example {
1010
return "home"
1111
}
1212
}
13-
14-
@Configuration(proxyBeanMethods = false)
15-
@Log
16-
class MvcConfiguration extends WebMvcConfigurerAdapter {
17-
18-
@Override
19-
void addInterceptors(InterceptorRegistry registry) {
20-
log.info "Registering interceptor"
21-
registry.addInterceptor(interceptor())
22-
}
23-
24-
@Bean
25-
HandlerInterceptor interceptor() {
26-
log.info "Creating interceptor"
27-
[
28-
postHandle: { request, response, handler, mav ->
29-
log.info "Intercepted: model=" + mav.model
30-
}
31-
] as HandlerInterceptorAdapter
32-
}
33-
}

spring-boot-project/spring-boot-cli/src/intTest/resources/settings.xml

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,17 @@
1717
<enabled>true</enabled>
1818
</snapshots>
1919
</repository>
20+
<repository>
21+
<id>thymeleaf-snapshot</id>
22+
<url>https://oss.sonatype.org/content/repositories/snapshots</url>
23+
<releases>
24+
<enabled>true</enabled>
25+
</releases>
26+
<snapshots>
27+
<enabled>true</enabled>
28+
</snapshots>
29+
</repository>
2030
</repositories>
2131
</profile>
2232
</profiles>
23-
</settings>
33+
</settings>

0 commit comments

Comments
 (0)