In your postman, I do not see X-XSRF-TOKEN header. If you’re not sending the XSRF-Token back to the server after fetching it from the cookie, you might wanna do it as shown in the end of the answer since it is one of the ways it is designed to protect against CSRF attacks and only works like that. In frameworks like angular, we can get it as a cookie from spring boot server and send it back as a header to differentiate malicious sites accessing the same URL, since such sites, inside the browser, cannot access the cookie associated with our genuine domain to send it back as a header.
Here is a simple working project which uses spring security 6 and crsf token with postman testing if it can help. It uses InMemoryUserDetailsManager, NoOpPasswordEncoder(Not recommended for production) and basic authentication.
SecurityConfig:
import java.util.function.Supplier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.ObjectPostProcessor;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configurers.CsrfConfigurer;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
import org.springframework.security.web.context.HttpSessionSecurityContextRepository;
import org.springframework.security.web.csrf.CookieCsrfTokenRepository;
import org.springframework.security.web.csrf.CsrfToken;
import org.springframework.security.web.csrf.CsrfTokenRequestHandler;
import org.springframework.security.web.csrf.XorCsrfTokenRequestAttributeHandler;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
@Configuration
public class ProjectSecurityConfig {
@Bean
SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
XorCsrfTokenRequestAttributeHandler delegate = new XorCsrfTokenRequestAttributeHandler();
delegate.setCsrfRequestAttributeName("_csrf");
CsrfTokenRequestHandler requestHandler = new CsrfTokenRequestHandler() {
@Override
public void handle(HttpServletRequest request, HttpServletResponse response,
Supplier<CsrfToken> csrfToken) {
delegate.handle(request, response, csrfToken);
}
};
return http
.cors().disable() // disabled cors for simplicity in this example in case of testing through a ui
.authorizeHttpRequests()
.requestMatchers("/error").permitAll()
.anyRequest().authenticated()
.and()
.csrf()
.csrfTokenRequestHandler(requestHandler)
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
.and().formLogin()
.and().httpBasic()
.and().build();
}
@Bean
InMemoryUserDetailsManager userDetailsService() {
UserDetails admin = User.withUsername("admin").password("pass").authorities("admin").build();
return new InMemoryUserDetailsManager(admin);
}
@Bean
PasswordEncoder passwordEncoder() {
return NoOpPasswordEncoder.getInstance();
}
}
Controller:
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import com.learning.entity.DataObject;
@RestController
public class TestController {
@PostMapping("/post")
public String post(@RequestBody DataObject dataObject) {
return "succesfull post";
}
}
DataObject Model:
public class DataObject {
private String data;
public String getData() {
return data;
}
public void setData(String data) {
this.data = data;
}
}
application.properties:
logging.level.org.springframework.security.web.csrf=TRACE
pom.xml:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.0.1</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.learning</groupId>
<artifactId>spring-security-3-csrf-example</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>spring-security-3-csrf-example</name>
<description>spring learning</description>
<properties>
<java.version>17</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
Test using CSRF token in postman:
First add basic auth credentials-

Add data object json body-

Send a mock request to the server to get a XSRF Cookie
Use this Cookie value as a header with name "X-XSRF-TOKEN"-

Testing it-

Note:- Since version 6, Spring Security does not create sessions for basic authentication by default so no Cookie for session will be returned in this example.
UPDATE :-
Here is an article on a more sophisticated way to send XSRF-TOKEN through postman as pointed by @OctaviaAdler in the comments. TLDR in case the link goes down:- Create an environment in postman and add the variable "xsrf-token" in it. Inside the request, add the header X-XSRF-TOKEN with the value set to "{{xsrf-token}}" (name of the environment variable in double curly braces without quotes). Then add the following script inside the "Tests" tab -
var xsrfCookie = postman.getResponseCookie("XSRF-TOKEN");
postman.setEnvironmentVariable("xsrf-token", xsrfCookie.value);