Spring MVC 和 Thymeleaf 中 CSRF 保護

Spring MVC,Spring Security
Remote
0
10:51 PM · Nov 29 ,2025

1. 簡介

Thymeleaf 是一款 Java 模板引擎,用於處理和創建 HTML、XML、JavaScript、CSS 和純文本。有關 Thymeleaf 和 Spring 的介紹,請查看這篇文檔。

在本文中,我們將討論如何防止跨站請求偽造 (CSRF) 攻擊在 Spring MVC 中使用 Thymeleaf 應用程序。具體來説,我們將測試 HTTP POST 方法的 CSRF 攻擊。

CSRF 是一種攻擊,強制用户在已認證的 Web 應用程序中執行未經授權的操作。

2. Maven 依賴項

首先,讓我們看看如何將 Thymeleaf 集成到 Spring 中所需的配置。 thymeleaf-spring 庫是我們的依賴項中必需的:

<dependency>
    <groupId>org.thymeleaf</groupId>
    <artifactId>thymeleaf</artifactId>
    <version>3.0.11.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.thymeleaf</groupId>
    <artifactId>thymeleaf-spring5</artifactId>
    <version>3.0.11.RELEASE</version>
</dependency>

請注意,對於 Spring 4 項目,必須使用 thymeleaf-spring4 庫,而不是 thymeleaf-spring5。 依賴項的最新版本可以在 這裏 找到。

此外,為了使用 Spring Security,我們需要添加以下依賴項:

<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-web</artifactId>
    <version>5.7.3</version>
</dependency>
<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-config</artifactId>
    <version>5.7.3</version>
</dependency>

兩個 Spring Security 相關庫的最新版本可以在 這裏這裏 找到。

@Configuration @EnableWebSecurity @EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true) public class WebMVCSecurity { @Bean public InMemoryUserDetailsManager userDetailsService() { UserDetails user = User.withUsername("user1") .password("{noop}user1Pass") .authorities("ROLE_USER") .build(); return new InMemoryUserDetailsManager(user); } @Bean public WebSecurityCustomizer webSecurityCustomizer() { return (web) -> web.ignoring() .antMatchers("/resources/**"); } @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http.authorizeRequests() .anyRequest() .authenticated() .and() .httpBasic(); return http.build(); } }

For more details and description of Security configuration, we refer to the Security with Spring series.

.csrf().disable()

<security:http auto-config="true" disable-url-rewriting="true" use-expressions="true"> <security:csrf /> <!-- Remaining configuration ... --> </security:http>

Please also note that if we are using a login or a logout page with a form that has any of PATCH, POST, PUT, or DELETE HTTP verbs, we need to always include the CSRF token as a hidden parameter manually in the code.

Example of using csrf token in a login form:

<form name="login" th:action="@{/login}" method="post"> 
...
<input type="hidden" 
th:name="${_csrf.parameterName}" 
th:value="${_csrf.token}" />
</form>

For the remaining forms, a CSRF token will be automatically added to forms with hidden input if you are using th:action:

<input 
  type="hidden" 
  name="_csrf"
  value="32e9ae18-76b9-4330-a8b6-08721283d048" /> 
<!-- Example token -->

4. Views Configuration

Let’s proceed to the main part of HTML files with form actions and testing procedure creation. In the first view, we try to add new students to the list:

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
	xmlns:th="http://www.thymeleaf.org">
<head>
<title>Add Student</title>
</head>
<body>
    <h1>Add Student</h1>
        <form action="#" th:action="@{/saveStudent}" th:object="${student}"
          method="post">
            <ul>
                <li th:errors="*{id}" />
                <li th:errors="*{name}" />
                <li th:errors="*{gender}" />
                <li th:errors="*{percentage}" />
            </ul>
    <!-- Remaining part of HTML -->
    </form>
</body>
</html>

In this view, we add a student to the list by providing id, name, gender and percentage (optionally, as stated in the form validation). Before we can execute this form, we need to provide a user and password to authenticate us in a web application.

4.1. Browser CSRF Attack Testing

Now we proceed to the second HTML view. The purpose of it is to try to do a CSRF attack:

<!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>

We know that the action URL is

Our request was denied because we sent a request without a CSRF token.

Please note that an HTTP session is used to store CSRF tokens. When the request is sent, Spring compares the generated token with the token stored in the session to confirm that the user is not hacked.

4.2. JUnit CSRF Attack Testing

If you don’t want to test the CSRF attack using a browser, you can also do it via a quick integration test; let’s start with the Spring config for that test:

@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@ContextConfiguration(classes = { 
  WebApp.class, WebMVCConfig.class, WebMVCSecurity.class, InitSecurity.class })
public class CsrfEnabledIntegrationTest {

    // configuration

}

And move on to the actual tests:

@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());
}

The first test will result in a forbidden status due to the missing CSRF token, whereas the second will be executed properly.

5. 結論

在本文中,我們討論瞭如何使用 Spring Security 和 Thymeleaf 框架來防止 CSRF 攻擊。

user avatar
0 位用戶收藏了這個故事!
收藏

發佈 評論

Some HTML is okay.