Spring Security + Thymeleaf (CSRF) (#695)
* 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
This commit is contained in:
parent
043a6bddc8
commit
b0a1959cf4
@ -14,6 +14,7 @@ import org.springframework.test.context.web.WebAppConfiguration;
|
|||||||
import org.springframework.test.web.servlet.MockMvc;
|
import org.springframework.test.web.servlet.MockMvc;
|
||||||
import org.springframework.test.web.servlet.request.RequestPostProcessor;
|
import org.springframework.test.web.servlet.request.RequestPostProcessor;
|
||||||
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
|
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
import org.springframework.web.context.WebApplicationContext;
|
import org.springframework.web.context.WebApplicationContext;
|
||||||
|
|
||||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||||
@ -21,6 +22,7 @@ import com.fasterxml.jackson.databind.ObjectMapper;
|
|||||||
|
|
||||||
@RunWith(SpringJUnit4ClassRunner.class)
|
@RunWith(SpringJUnit4ClassRunner.class)
|
||||||
@WebAppConfiguration
|
@WebAppConfiguration
|
||||||
|
@Transactional
|
||||||
public class CsrfAbstractIntegrationTest {
|
public class CsrfAbstractIntegrationTest {
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
|
@ -8,24 +8,24 @@
|
|||||||
<properties>
|
<properties>
|
||||||
<java-version>1.7</java-version>
|
<java-version>1.7</java-version>
|
||||||
<!-- spring -->
|
<!-- spring -->
|
||||||
<org.springframework-version>4.1.8.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>2.1.4.RELEASE</org.thymeleaf-version>
|
<org.thymeleaf-version>2.1.4.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>
|
||||||
@ -45,6 +45,17 @@
|
|||||||
<artifactId>spring-webmvc</artifactId>
|
<artifactId>spring-webmvc</artifactId>
|
||||||
<version>${org.springframework-version}</version>
|
<version>${org.springframework-version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<!-- Spring Security -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.security</groupId>
|
||||||
|
<artifactId>spring-security-web</artifactId>
|
||||||
|
<version>4.1.3.RELEASE</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.security</groupId>
|
||||||
|
<artifactId>spring-security-config</artifactId>
|
||||||
|
<version>4.1.3.RELEASE</version>
|
||||||
|
</dependency>
|
||||||
<!-- Thymeleaf -->
|
<!-- Thymeleaf -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.thymeleaf</groupId>
|
<groupId>org.thymeleaf</groupId>
|
||||||
@ -57,29 +68,29 @@
|
|||||||
<version>${org.thymeleaf-version}</version>
|
<version>${org.thymeleaf-version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<!-- Logging -->
|
<!-- Logging -->
|
||||||
<!-- logging -->
|
<!-- logging -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.slf4j</groupId>
|
<groupId>org.slf4j</groupId>
|
||||||
<artifactId>slf4j-api</artifactId>
|
<artifactId>slf4j-api</artifactId>
|
||||||
<version>${org.slf4j.version}</version>
|
<version>${org.slf4j.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>ch.qos.logback</groupId>
|
<groupId>ch.qos.logback</groupId>
|
||||||
<artifactId>logback-classic</artifactId>
|
<artifactId>logback-classic</artifactId>
|
||||||
<version>${logback.version}</version>
|
<version>${logback.version}</version>
|
||||||
<!-- <scope>runtime</scope> -->
|
<!-- <scope>runtime</scope> -->
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.slf4j</groupId>
|
<groupId>org.slf4j</groupId>
|
||||||
<artifactId>jcl-over-slf4j</artifactId>
|
<artifactId>jcl-over-slf4j</artifactId>
|
||||||
<version>${org.slf4j.version}</version>
|
<version>${org.slf4j.version}</version>
|
||||||
<!-- <scope>runtime</scope> --> <!-- some spring dependencies need to compile against jcl -->
|
<!-- <scope>runtime</scope> --> <!-- some spring dependencies need to compile against jcl -->
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency> <!-- needed to bridge to slf4j for projects that use the log4j APIs directly -->
|
<dependency> <!-- needed to bridge to slf4j for projects that use the log4j APIs directly -->
|
||||||
<groupId>org.slf4j</groupId>
|
<groupId>org.slf4j</groupId>
|
||||||
<artifactId>log4j-over-slf4j</artifactId>
|
<artifactId>log4j-over-slf4j</artifactId>
|
||||||
<version>${org.slf4j.version}</version>
|
<version>${org.slf4j.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<!-- Servlet -->
|
<!-- Servlet -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>javax.servlet</groupId>
|
<groupId>javax.servlet</groupId>
|
||||||
@ -98,6 +109,31 @@
|
|||||||
<artifactId>hibernate-validator</artifactId>
|
<artifactId>hibernate-validator</artifactId>
|
||||||
<version>${org.hibernate-version}</version>
|
<version>${org.hibernate-version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<!-- test scoped -->
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework</groupId>
|
||||||
|
<artifactId>spring-test</artifactId>
|
||||||
|
<version>4.1.3.RELEASE</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- https://mvnrepository.com/artifact/org.springframework.security/spring-security-test -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.security</groupId>
|
||||||
|
<artifactId>spring-security-test</artifactId>
|
||||||
|
<version>4.1.3.RELEASE</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- https://mvnrepository.com/artifact/junit/junit -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>junit</groupId>
|
||||||
|
<artifactId>junit</artifactId>
|
||||||
|
<version>4.12</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
</dependencies>
|
</dependencies>
|
||||||
<build>
|
<build>
|
||||||
<plugins>
|
<plugins>
|
||||||
@ -129,25 +165,25 @@
|
|||||||
</systemPropertyVariables>
|
</systemPropertyVariables>
|
||||||
</configuration>
|
</configuration>
|
||||||
</plugin>
|
</plugin>
|
||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.codehaus.cargo</groupId>
|
<groupId>org.codehaus.cargo</groupId>
|
||||||
<artifactId>cargo-maven2-plugin</artifactId>
|
<artifactId>cargo-maven2-plugin</artifactId>
|
||||||
<version>${cargo-maven2-plugin.version}</version>
|
<version>${cargo-maven2-plugin.version}</version>
|
||||||
<configuration>
|
<configuration>
|
||||||
<wait>true</wait>
|
<wait>true</wait>
|
||||||
<container>
|
<container>
|
||||||
<containerId>jetty8x</containerId>
|
<containerId>jetty8x</containerId>
|
||||||
<type>embedded</type>
|
<type>embedded</type>
|
||||||
<systemProperties>
|
<systemProperties>
|
||||||
</systemProperties>
|
</systemProperties>
|
||||||
</container>
|
</container>
|
||||||
<configuration>
|
<configuration>
|
||||||
<properties>
|
<properties>
|
||||||
<cargo.servlet.port>8082</cargo.servlet.port>
|
<cargo.servlet.port>8082</cargo.servlet.port>
|
||||||
</properties>
|
</properties>
|
||||||
</configuration>
|
</configuration>
|
||||||
</configuration>
|
</configuration>
|
||||||
</plugin>
|
</plugin>
|
||||||
</plugins>
|
</plugins>
|
||||||
</build>
|
</build>
|
||||||
</project>
|
</project>
|
||||||
|
@ -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);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -20,7 +20,7 @@ public class WebApp extends AbstractAnnotationConfigDispatcherServletInitializer
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Class<?>[] getServletConfigClasses() {
|
protected Class<?>[] getServletConfigClasses() {
|
||||||
return new Class<?>[] { WebMVCConfig.class };
|
return new Class<?>[] { WebMVCConfig.class, WebMVCSecurity.class, InitSecurity.class };
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
package com.baeldung.thymeleaf.config;
|
package com.baeldung.thymeleaf.config;
|
||||||
|
|
||||||
import com.baeldung.thymeleaf.formatter.NameFormatter;
|
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.ComponentScan;
|
import org.springframework.context.annotation.ComponentScan;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
@ -14,6 +13,8 @@ import org.thymeleaf.spring4.SpringTemplateEngine;
|
|||||||
import org.thymeleaf.spring4.view.ThymeleafViewResolver;
|
import org.thymeleaf.spring4.view.ThymeleafViewResolver;
|
||||||
import org.thymeleaf.templateresolver.ServletContextTemplateResolver;
|
import org.thymeleaf.templateresolver.ServletContextTemplateResolver;
|
||||||
|
|
||||||
|
import com.baeldung.thymeleaf.formatter.NameFormatter;
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
@EnableWebMvc
|
@EnableWebMvc
|
||||||
@ComponentScan({ "com.baeldung.thymeleaf" })
|
@ComponentScan({ "com.baeldung.thymeleaf" })
|
||||||
|
@ -0,0 +1,50 @@
|
|||||||
|
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("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()
|
||||||
|
.and()
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,12 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<form action="http://localhost:8080/spring-thymeleaf/saveStudent" method="post">
|
||||||
|
<input type="hidden" name="payload" value="CSRF attack!"/>
|
||||||
|
<input type="submit" />
|
||||||
|
</form>
|
||||||
|
</body>
|
||||||
|
</html>
|
@ -0,0 +1,63 @@
|
|||||||
|
package org.baeldung.security.csrf;
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
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.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 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 CsrfEnabledIntegrationTest {
|
||||||
|
|
||||||
|
@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 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());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user