Thymeleaf decorator (#747)

* Expression-Based Access Control

PermitAll, hasRole, hasAnyRole etc.
I modified classes regards to Security

* Added test cases for Spring Security Expressions

* Handler Interceptor - logging example

* Test for logger interceptor

* Removed conflicted part

* UserInterceptor (adding user information to model)

* Spring Handler Interceptor - session timers

* Spring Security CSRF attack protection with Thymeleaf

* Fix and();

* Logger update

* Changed config for Thymeleaf

* Thymeleaf Natural Processing and Inlining

* Expression Utility Objects, Thymeleaf

* listOfStudents edited

* Thymeleaf layout decorators
This commit is contained in:
maibin 2016-10-13 07:08:59 +02:00 committed by Grzegorz Piwowarek
parent 15f5a9eca1
commit 08c9791cb4
7 changed files with 296 additions and 176 deletions

View File

@ -5,186 +5,192 @@
<artifactId>spring-thymeleaf</artifactId> <artifactId>spring-thymeleaf</artifactId>
<version>0.1-SNAPSHOT</version> <version>0.1-SNAPSHOT</version>
<packaging>war</packaging> <packaging>war</packaging>
<properties> <properties>
<java-version>1.8</java-version> <java-version>1.8</java-version>
<!-- spring --> <!-- spring -->
<org.springframework-version>4.3.3.RELEASE</org.springframework-version> <org.springframework-version>4.3.3.RELEASE</org.springframework-version>
<javax.servlet-version>3.0.1</javax.servlet-version> <javax.servlet-version>3.0.1</javax.servlet-version>
<!-- logging --> <!-- logging -->
<org.slf4j.version>1.7.12</org.slf4j.version> <org.slf4j.version>1.7.12</org.slf4j.version>
<logback.version>1.1.3</logback.version> <logback.version>1.1.3</logback.version>
<!-- thymeleaf --> <!-- thymeleaf -->
<org.thymeleaf-version>3.0.1.RELEASE</org.thymeleaf-version> <org.thymeleaf-version>3.0.1.RELEASE</org.thymeleaf-version>
<!-- validation --> <!-- validation -->
<javax.validation-version>1.1.0.Final</javax.validation-version> <javax.validation-version>1.1.0.Final</javax.validation-version>
<org.hibernate-version>5.1.2.Final</org.hibernate-version> <org.hibernate-version>5.1.2.Final</org.hibernate-version>
<!-- Maven plugins --> <!-- Maven plugins -->
<maven-compiler-plugin.version>3.5.1</maven-compiler-plugin.version> <maven-compiler-plugin.version>3.5.1</maven-compiler-plugin.version>
<maven-war-plugin.version>2.6</maven-war-plugin.version> <maven-war-plugin.version>2.6</maven-war-plugin.version>
<maven-surefire-plugin.version>2.19.1</maven-surefire-plugin.version> <maven-surefire-plugin.version>2.19.1</maven-surefire-plugin.version>
<cargo-maven2-plugin.version>1.4.18</cargo-maven2-plugin.version> <cargo-maven2-plugin.version>1.4.18</cargo-maven2-plugin.version>
</properties> </properties>
<dependencies> <dependencies>
<!-- Spring --> <!-- Spring -->
<dependency> <dependency>
<groupId>org.springframework</groupId> <groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId> <artifactId>spring-context</artifactId>
<version>${org.springframework-version}</version> <version>${org.springframework-version}</version>
<exclusions> <exclusions>
<!-- Exclude Commons Logging in favor of SLF4j --> <!-- Exclude Commons Logging in favor of SLF4j -->
<exclusion> <exclusion>
<groupId>commons-logging</groupId> <groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId> <artifactId>commons-logging</artifactId>
</exclusion> </exclusion>
</exclusions> </exclusions>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.springframework</groupId> <groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId> <artifactId>spring-webmvc</artifactId>
<version>${org.springframework-version}</version> <version>${org.springframework-version}</version>
</dependency> </dependency>
<!-- Spring Security --> <!-- Spring Security -->
<dependency> <dependency>
<groupId>org.springframework.security</groupId> <groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId> <artifactId>spring-security-web</artifactId>
<version>4.1.3.RELEASE</version> <version>4.1.3.RELEASE</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.springframework.security</groupId> <groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId> <artifactId>spring-security-config</artifactId>
<version>4.1.3.RELEASE</version> <version>4.1.3.RELEASE</version>
</dependency> </dependency>
<!-- Thymeleaf --> <!-- Thymeleaf -->
<dependency> <dependency>
<groupId>org.thymeleaf</groupId> <groupId>org.thymeleaf</groupId>
<artifactId>thymeleaf</artifactId> <artifactId>thymeleaf</artifactId>
<version>${org.thymeleaf-version}</version> <version>${org.thymeleaf-version}</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.thymeleaf</groupId> <groupId>org.thymeleaf</groupId>
<artifactId>thymeleaf-spring4</artifactId> <artifactId>thymeleaf-spring4</artifactId>
<version>${org.thymeleaf-version}</version> <version>${org.thymeleaf-version}</version>
</dependency> </dependency>
<!-- Logging --> <!-- https://mvnrepository.com/artifact/nz.net.ultraq.thymeleaf/thymeleaf-layout-dialect -->
<dependency> <dependency>
<groupId>org.slf4j</groupId> <groupId>nz.net.ultraq.thymeleaf</groupId>
<artifactId>slf4j-api</artifactId> <artifactId>thymeleaf-layout-dialect</artifactId>
<version>${org.slf4j.version}</version> <version>2.0.4</version>
</dependency> </dependency>
<dependency> <!-- Logging -->
<groupId>ch.qos.logback</groupId> <dependency>
<artifactId>logback-classic</artifactId> <groupId>org.slf4j</groupId>
<version>${logback.version}</version> <artifactId>slf4j-api</artifactId>
<!-- <scope>runtime</scope> --> <version>${org.slf4j.version}</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.slf4j</groupId> <groupId>ch.qos.logback</groupId>
<artifactId>jcl-over-slf4j</artifactId> <artifactId>logback-classic</artifactId>
<version>${org.slf4j.version}</version> <version>${logback.version}</version>
<!-- <scope>runtime</scope> --> <!-- some spring dependencies need to compile against jcl --> <!-- <scope>runtime</scope> -->
</dependency> </dependency>
<dependency> <!-- needed to bridge to slf4j for projects that use the log4j APIs directly --> <dependency>
<groupId>org.slf4j</groupId> <groupId>org.slf4j</groupId>
<artifactId>log4j-over-slf4j</artifactId> <artifactId>jcl-over-slf4j</artifactId>
<version>${org.slf4j.version}</version> <version>${org.slf4j.version}</version>
</dependency> <!-- <scope>runtime</scope> --> <!-- some spring dependencies need to compile against jcl -->
<!-- Servlet --> </dependency>
<dependency> <dependency> <!-- needed to bridge to slf4j for projects that use the log4j APIs directly -->
<groupId>javax.servlet</groupId> <groupId>org.slf4j</groupId>
<artifactId>javax.servlet-api</artifactId> <artifactId>log4j-over-slf4j</artifactId>
<version>${javax.servlet-version}</version> <version>${org.slf4j.version}</version>
<scope>provided</scope> </dependency>
</dependency> <!-- Servlet -->
<!-- Validation --> <dependency>
<dependency> <groupId>javax.servlet</groupId>
<groupId>javax.validation</groupId> <artifactId>javax.servlet-api</artifactId>
<artifactId>validation-api</artifactId> <version>${javax.servlet-version}</version>
<version>${javax.validation-version}</version> <scope>provided</scope>
</dependency> </dependency>
<dependency> <!-- Validation -->
<groupId>org.hibernate</groupId> <dependency>
<artifactId>hibernate-validator</artifactId> <groupId>javax.validation</groupId>
<version>${org.hibernate-version}</version> <artifactId>validation-api</artifactId>
</dependency> <version>${javax.validation-version}</version>
<!-- test scoped --> </dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>${org.hibernate-version}</version>
</dependency>
<!-- test scoped -->
<dependency> <dependency>
<groupId>org.springframework</groupId> <groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId> <artifactId>spring-test</artifactId>
<version>4.1.3.RELEASE</version> <version>4.1.3.RELEASE</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<!-- https://mvnrepository.com/artifact/org.springframework.security/spring-security-test --> <!-- https://mvnrepository.com/artifact/org.springframework.security/spring-security-test -->
<dependency> <dependency>
<groupId>org.springframework.security</groupId> <groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId> <artifactId>spring-security-test</artifactId>
<version>4.1.3.RELEASE</version> <version>4.1.3.RELEASE</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<!-- https://mvnrepository.com/artifact/junit/junit --> <!-- https://mvnrepository.com/artifact/junit/junit -->
<dependency> <dependency>
<groupId>junit</groupId> <groupId>junit</groupId>
<artifactId>junit</artifactId> <artifactId>junit</artifactId>
<version>4.12</version> <version>4.12</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
</dependencies> </dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>${maven-compiler-plugin.version}</version>
<configuration>
<source>${java-version}</source>
<target>${java-version}</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>${maven-war-plugin.version}</version>
<configuration>
<failOnMissingWebXml>false</failOnMissingWebXml>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>${maven-surefire-plugin.version}</version>
<configuration>
<excludes>
</excludes>
<systemPropertyVariables>
</systemPropertyVariables>
</configuration>
</plugin>
<plugin>
<groupId>org.codehaus.cargo</groupId>
<artifactId>cargo-maven2-plugin</artifactId>
<version>${cargo-maven2-plugin.version}</version>
<configuration>
<wait>true</wait>
<container>
<containerId>jetty8x</containerId>
<type>embedded</type>
<systemProperties>
</systemProperties>
</container>
<configuration>
<properties>
<cargo.servlet.port>8082</cargo.servlet.port>
</properties>
</configuration>
</configuration>
</plugin>
</plugins>
</build>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>${maven-compiler-plugin.version}</version>
<configuration>
<source>${java-version}</source>
<target>${java-version}</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>${maven-war-plugin.version}</version>
<configuration>
<failOnMissingWebXml>false</failOnMissingWebXml>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>${maven-surefire-plugin.version}</version>
<configuration>
<excludes>
</excludes>
<systemPropertyVariables>
</systemPropertyVariables>
</configuration>
</plugin>
<plugin>
<groupId>org.codehaus.cargo</groupId>
<artifactId>cargo-maven2-plugin</artifactId>
<version>${cargo-maven2-plugin.version}</version>
<configuration>
<wait>true</wait>
<container>
<containerId>jetty8x</containerId>
<type>embedded</type>
<systemProperties>
</systemProperties>
</container>
<configuration>
<properties>
<cargo.servlet.port>8082</cargo.servlet.port>
</properties>
</configuration>
</configuration>
</plugin>
</plugins>
</build>
</project> </project>

View File

@ -22,6 +22,9 @@ import org.thymeleaf.templateresolver.ITemplateResolver;
import com.baeldung.thymeleaf.formatter.NameFormatter; import com.baeldung.thymeleaf.formatter.NameFormatter;
import com.baeldung.thymeleaf.utils.ArrayUtil; import com.baeldung.thymeleaf.utils.ArrayUtil;
import nz.net.ultraq.thymeleaf.LayoutDialect;
import nz.net.ultraq.thymeleaf.decorators.strategies.GroupingStrategy;
@Configuration @Configuration
@EnableWebMvc @EnableWebMvc
@ -70,6 +73,7 @@ public class WebMVCConfig extends WebMvcConfigurerAdapter implements Application
private TemplateEngine templateEngine(ITemplateResolver templateResolver) { private TemplateEngine templateEngine(ITemplateResolver templateResolver) {
SpringTemplateEngine engine = new SpringTemplateEngine(); SpringTemplateEngine engine = new SpringTemplateEngine();
engine.addDialect(new LayoutDialect(new GroupingStrategy()));
engine.setTemplateResolver(templateResolver); engine.setTemplateResolver(templateResolver);
return engine; return engine;
} }

View File

@ -0,0 +1,16 @@
package com.baeldung.thymeleaf.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
@Controller
public class LayoutDialectController {
@RequestMapping(value = "/layout", method = RequestMethod.GET)
public String getNewPage(Model model) {
return "content.html";
}
}

View File

@ -0,0 +1,16 @@
<!DOCTYPE html>
<html xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
layout:decorate="~{template.html}">
<head>
<title>Layout Dialect Example</title>
</head>
<body>
<section layout:fragment="custom-content">
<p>This is a custom content that you can provide</p>
</section>
<footer>
<p layout:fragment="custom-footer">This is some footer content
that you can change</p>
</footer>
</body>
</html>

View File

@ -6,7 +6,7 @@
</head> </head>
<script type="text/javascript" <script type="text/javascript"
src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.1/jquery.js"></script> src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.1/jquery.js"></script>
<script th:inline="javascript"> <script>
$(document).ready(function() { $(document).ready(function() {
$.ajax({ $.ajax({
url : "/spring-thymeleaf/js", url : "/spring-thymeleaf/js",

View File

@ -0,0 +1,20 @@
<!DOCTYPE html>
<html xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout">
<head>
<title layout:title-pattern="$LAYOUT_TITLE - $CONTENT_TITLE">Baeldung</title>
<script type="text/javascript"
src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.1/jquery.js"></script>
</head>
<body>
<header>
<h1>New dialect example</h1>
</header>
<section layout:fragment="custom-content">
<p>Your page content goes here</p>
</section>
<footer>
<p>My custom footer</p>
<p layout:fragment="custom-footer">Your custom footer here</p>
</footer>
</body>
</html>

View File

@ -0,0 +1,58 @@
package com.baeldung.thymeleaf.controller;
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf;
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.user;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.view;
import javax.servlet.Filter;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.mock.web.MockHttpSession;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.RequestPostProcessor;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;
import com.baeldung.thymeleaf.config.InitSecurity;
import com.baeldung.thymeleaf.config.WebApp;
import com.baeldung.thymeleaf.config.WebMVCConfig;
import com.baeldung.thymeleaf.config.WebMVCSecurity;
@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@ContextConfiguration(classes = { WebApp.class, WebMVCConfig.class, WebMVCSecurity.class, InitSecurity.class })
public class LayoutDialectControllerTest {
@Autowired
WebApplicationContext wac;
@Autowired
MockHttpSession session;
private MockMvc mockMvc;
@Autowired
private Filter springSecurityFilterChain;
protected RequestPostProcessor testUser() {
return user("user1").password("user1Pass").roles("USER");
}
@Before
public void setup() {
mockMvc = MockMvcBuilders.webAppContextSetup(wac).addFilters(springSecurityFilterChain).build();
}
@Test
public void testGetDates() throws Exception{
mockMvc.perform(get("/layout").with(testUser()).with(csrf())).andExpect(status().isOk()).andExpect(view().name("content.html"));
}
}