diff --git a/spring-web-modules/spring-thymeleaf-4/README.md b/spring-web-modules/spring-thymeleaf-4/README.md index 2ed70f1c49..f8dc4c8b4e 100644 --- a/spring-web-modules/spring-thymeleaf-4/README.md +++ b/spring-web-modules/spring-thymeleaf-4/README.md @@ -3,6 +3,7 @@ This module contains articles about Spring with Thymeleaf ### Relevant Articles: +- [CSRF Protection with Spring MVC and Thymeleaf](https://www.baeldung.com/csrf-thymeleaf-with-spring-security) - [Conditionals in Thymeleaf](https://www.baeldung.com/spring-thymeleaf-conditionals) - [Iteration in Thymeleaf](https://www.baeldung.com/thymeleaf-iteration) - [Spring with Thymeleaf Pagination for a List](https://www.baeldung.com/spring-thymeleaf-pagination) diff --git a/spring-web-modules/spring-thymeleaf-4/pom.xml b/spring-web-modules/spring-thymeleaf-4/pom.xml index f307e6fc26..44577078b0 100644 --- a/spring-web-modules/spring-thymeleaf-4/pom.xml +++ b/spring-web-modules/spring-thymeleaf-4/pom.xml @@ -48,6 +48,17 @@ hibernate-validator ${hibernate-validator.version} + + + org.springframework.security + spring-security-web + ${spring-security.version} + + + org.springframework.security + spring-security-config + ${spring-security.version} + org.thymeleaf @@ -83,6 +94,12 @@ ${spring.version} test + + org.springframework.security + spring-security-test + ${spring-security.version} + test + diff --git a/spring-web-modules/spring-thymeleaf-4/src/main/java/com/baeldung/thymeleaf/config/InitSecurity.java b/spring-web-modules/spring-thymeleaf-4/src/main/java/com/baeldung/thymeleaf/config/InitSecurity.java new file mode 100644 index 0000000000..956db4a0e5 --- /dev/null +++ b/spring-web-modules/spring-thymeleaf-4/src/main/java/com/baeldung/thymeleaf/config/InitSecurity.java @@ -0,0 +1,11 @@ +package com.baeldung.thymeleaf.config; + +import org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer; + +public class InitSecurity extends AbstractSecurityWebApplicationInitializer { + + public InitSecurity() { + super(WebMVCSecurity.class); + + } +} diff --git a/spring-web-modules/spring-thymeleaf-4/src/main/java/com/baeldung/thymeleaf/config/WebMVCSecurity.java b/spring-web-modules/spring-thymeleaf-4/src/main/java/com/baeldung/thymeleaf/config/WebMVCSecurity.java new file mode 100644 index 0000000000..ea51ca3cd9 --- /dev/null +++ b/spring-web-modules/spring-thymeleaf-4/src/main/java/com/baeldung/thymeleaf/config/WebMVCSecurity.java @@ -0,0 +1,43 @@ +package com.baeldung.thymeleaf.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; +import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.builders.WebSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; + +@Configuration +@EnableWebSecurity +@EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true) +public class WebMVCSecurity extends WebSecurityConfigurerAdapter { + + @Bean + @Override + public AuthenticationManager authenticationManagerBean() throws Exception { + return super.authenticationManagerBean(); + } + + public WebMVCSecurity() { + super(); + } + + @Override + protected void configure(final AuthenticationManagerBuilder auth) throws Exception { + auth.inMemoryAuthentication().withUser("user1").password("{noop}user1Pass").authorities("ROLE_USER"); + } + + @Override + public void configure(final WebSecurity web) throws Exception { + web.ignoring().antMatchers("/resources/**"); + } + + @Override + protected void configure(final HttpSecurity http) throws Exception { + http.authorizeRequests().anyRequest().authenticated().and().httpBasic(); + } + +} diff --git a/spring-web-modules/spring-thymeleaf/src/main/webapp/WEB-INF/views/csrfAttack.html b/spring-web-modules/spring-thymeleaf-4/src/main/webapp/WEB-INF/views/csrfAttack.html similarity index 100% rename from spring-web-modules/spring-thymeleaf/src/main/webapp/WEB-INF/views/csrfAttack.html rename to spring-web-modules/spring-thymeleaf-4/src/main/webapp/WEB-INF/views/csrfAttack.html diff --git a/spring-web-modules/spring-thymeleaf-4/src/test/java/com/baeldung/thymeleaf/controller/ControllerIntegrationTest.java b/spring-web-modules/spring-thymeleaf-4/src/test/java/com/baeldung/thymeleaf/controller/ControllerIntegrationTest.java new file mode 100644 index 0000000000..cbbcdc0868 --- /dev/null +++ b/spring-web-modules/spring-thymeleaf-4/src/test/java/com/baeldung/thymeleaf/controller/ControllerIntegrationTest.java @@ -0,0 +1,63 @@ +package com.baeldung.thymeleaf.controller; + +import com.baeldung.thymeleaf.config.WebApp; +import com.baeldung.thymeleaf.config.WebMVCConfig; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.MediaType; +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 javax.servlet.Filter; + +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.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.view; + +@RunWith(SpringJUnit4ClassRunner.class) +@WebAppConfiguration +@ContextConfiguration(classes = { WebApp.class, WebMVCConfig.class}) +public class ControllerIntegrationTest { + + @Autowired + WebApplicationContext wac; + + private MockMvc mockMvc; + + @Autowired + private Filter springSecurityFilterChain; + + private RequestPostProcessor testUser() { + return user("user1").password("user1Pass").roles("USER"); + } + + @Before + public void setup() { + mockMvc = MockMvcBuilders.webAppContextSetup(wac).addFilters(springSecurityFilterChain).build(); + } + + @Test + public void testTeachers() throws Exception { + mockMvc.perform(get("/listTeachers").with(testUser()).with(csrf())).andExpect(status().isOk()).andExpect(view().name("listTeachers.html")); + } + + @Test + public void addStudentWithoutCSRF() throws Exception { + mockMvc.perform(post("/saveStudent").contentType(MediaType.APPLICATION_JSON).param("id", "1234567").param("name", "Joe").param("gender", "M").with(testUser())).andExpect(status().isForbidden()); + } + + @Test + public void addStudentWithCSRF() throws Exception { + mockMvc.perform(post("/saveStudent").contentType(MediaType.APPLICATION_JSON).param("id", "1234567").param("name", "Joe").param("gender", "M").with(testUser()).with(csrf())).andExpect(status().isOk()); + } +} diff --git a/spring-web-modules/spring-thymeleaf-4/src/test/java/com/baeldung/thymeleaf/security/csrf/CsrfEnabledIntegrationTest.java b/spring-web-modules/spring-thymeleaf-4/src/test/java/com/baeldung/thymeleaf/security/csrf/CsrfEnabledIntegrationTest.java new file mode 100644 index 0000000000..08a31cfa83 --- /dev/null +++ b/spring-web-modules/spring-thymeleaf-4/src/test/java/com/baeldung/thymeleaf/security/csrf/CsrfEnabledIntegrationTest.java @@ -0,0 +1,62 @@ +package com.baeldung.thymeleaf.security.csrf; + +import com.baeldung.thymeleaf.config.InitSecurity; +import com.baeldung.thymeleaf.config.WebApp; +import com.baeldung.thymeleaf.config.WebMVCConfig; +import com.baeldung.thymeleaf.config.WebMVCSecurity; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.MediaType; +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 javax.servlet.Filter; + +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.post; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@RunWith(SpringJUnit4ClassRunner.class) +@WebAppConfiguration +@ContextConfiguration(classes = { WebApp.class, WebMVCConfig.class, WebMVCSecurity.class, InitSecurity.class }) +public class CsrfEnabledIntegrationTest { + + @Autowired + WebApplicationContext wac; + @Autowired + MockHttpSession session; + + private MockMvc mockMvc; + + @Autowired + private Filter springSecurityFilterChain; + + private RequestPostProcessor testUser() { + return user("user1").password("user1Pass").roles("USER"); + } + + @Before + public void setup() { + mockMvc = MockMvcBuilders.webAppContextSetup(wac).addFilters(springSecurityFilterChain).build(); + } + + @Test + public void addStudentWithoutCSRF() throws Exception { + mockMvc.perform(post("/saveStudent").contentType(MediaType.APPLICATION_JSON).param("id", "1234567").param("name", "Joe").param("gender", "M").with(testUser())).andExpect(status().isForbidden()); + } + + @Test + public void addStudentWithCSRF() throws Exception { + mockMvc.perform(post("/saveStudent").contentType(MediaType.APPLICATION_JSON).param("id", "1234567").param("name", "Joe").param("gender", "M").with(testUser()).with(csrf())).andExpect(status().isOk()); + } + +} diff --git a/spring-web-modules/spring-thymeleaf/README.md b/spring-web-modules/spring-thymeleaf/README.md index e83ef332e0..12eb97b4a2 100644 --- a/spring-web-modules/spring-thymeleaf/README.md +++ b/spring-web-modules/spring-thymeleaf/README.md @@ -4,7 +4,6 @@ This module contains articles about Spring with Thymeleaf ### Relevant Articles: - [Introduction to Using Thymeleaf in Spring](https://www.baeldung.com/thymeleaf-in-spring-mvc) -- [CSRF Protection with Spring MVC and Thymeleaf](https://www.baeldung.com/csrf-thymeleaf-with-spring-security) - [Thymeleaf: Custom Layout Dialect](https://www.baeldung.com/thymeleaf-spring-layouts) - [Spring and Thymeleaf 3: Expressions](https://www.baeldung.com/spring-thymeleaf-3-expressions) - [Spring MVC + Thymeleaf 3.0: New Features](https://www.baeldung.com/spring-thymeleaf-3) diff --git a/spring-web-modules/spring-thymeleaf/src/test/java/com/baeldung/thymeleaf/controller/ExpressionUtilityObjectsControllerIntegrationTest.java b/spring-web-modules/spring-thymeleaf/src/test/java/com/baeldung/thymeleaf/controller/ExpressionUtilityObjectsControllerIntegrationTest.java index 4a2a9974f1..462e9e8c21 100644 --- a/spring-web-modules/spring-thymeleaf/src/test/java/com/baeldung/thymeleaf/controller/ExpressionUtilityObjectsControllerIntegrationTest.java +++ b/spring-web-modules/spring-thymeleaf/src/test/java/com/baeldung/thymeleaf/controller/ExpressionUtilityObjectsControllerIntegrationTest.java @@ -59,10 +59,5 @@ public class ExpressionUtilityObjectsControllerIntegrationTest { public void testDates() throws Exception { mockMvc.perform(get("/dates").with(testUser()).with(csrf())).andExpect(status().isOk()).andExpect(view().name("dates.html")); } - - @Test - public void testTeachers() throws Exception { - mockMvc.perform(get("/listTeachers").with(testUser()).with(csrf())).andExpect(status().isOk()).andExpect(view().name("listTeachers.html")); - } } diff --git a/spring-web-modules/spring-thymeleaf/src/test/java/com/baeldung/thymeleaf/security/csrf/CsrfEnabledIntegrationTest.java b/spring-web-modules/spring-thymeleaf/src/test/java/com/baeldung/thymeleaf/security/csrf/CsrfEnabledIntegrationTest.java index a7c9fb4ae4..1160250877 100644 --- a/spring-web-modules/spring-thymeleaf/src/test/java/com/baeldung/thymeleaf/security/csrf/CsrfEnabledIntegrationTest.java +++ b/spring-web-modules/spring-thymeleaf/src/test/java/com/baeldung/thymeleaf/security/csrf/CsrfEnabledIntegrationTest.java @@ -49,16 +49,6 @@ public class CsrfEnabledIntegrationTest { mockMvc = MockMvcBuilders.webAppContextSetup(wac).addFilters(springSecurityFilterChain).build(); } - @Test - public void addStudentWithoutCSRF() throws Exception { - mockMvc.perform(post("/saveStudent").contentType(MediaType.APPLICATION_JSON).param("id", "1234567").param("name", "Joe").param("gender", "M").with(testUser())).andExpect(status().isForbidden()); - } - - @Test - public void addStudentWithCSRF() throws Exception { - mockMvc.perform(post("/saveStudent").contentType(MediaType.APPLICATION_JSON).param("id", "1234567").param("name", "Joe").param("gender", "M").with(testUser()).with(csrf())).andExpect(status().isOk()); - } - @Test public void htmlInliningTest() throws Exception { mockMvc.perform(get("/html").with(testUser()).with(csrf())).andExpect(status().isOk()).andExpect(view().name("inliningExample.html"));