Mastering CSRF Protection in Spring Security with In‑Memory Users
This tutorial walks through configuring Spring Security 5.4 on Spring Boot 2.4, demonstrating how to set up in‑memory user credentials, handle CSRF tokens during GET and POST requests, and customize the security filter chain to enable or disable CSRF protection.
Environment: Spring Boot 2.4.12 + Spring Security 5.4.9.
Adding the Dependency
<code><dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
</code>Custom User Configuration (application.yml)
<code>spring:
security:
user:
name: admin
password: 123456
</code>Defining the Controller
<code>@RestController
@RequestMapping("/demos")
public class DemoController {
@GetMapping("home")
public Object home() {
return "demos home";
}
@PostMapping("post")
public Object post() {
return "demos post";
}
}
</code>Access http://localhost:8080/demos/home – you will be redirected to the default login page.
Log in with the credentials defined above (admin/123456).
Testing POST Endpoint
When calling the POST endpoint via Postman, you must first log in through /login . After a successful login, the GET endpoint works, but the POST request returns 403 because the CSRF token is missing.
The login page includes a hidden _csrf field. If you submit a POST request without this field, authentication fails.
Include the _csrf parameter in your POST request to succeed.
How CSRF Protection Works
Spring Security adds a CsrfFilter that retrieves a token from the session (or generates one) and validates it on state‑changing requests.
<code>public final class CsrfFilter extends OncePerRequestFilter {
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
CsrfToken csrfToken = this.tokenRepository.loadToken(request);
boolean missingToken = (csrfToken == null);
if (missingToken) {
csrfToken = this.tokenRepository.generateToken(request);
this.tokenRepository.saveToken(csrfToken, request, response);
}
if (!this.requireCsrfProtectionMatcher.matches(request)) {
filterChain.doFilter(request, response);
return;
}
String actualToken = request.getHeader(csrfToken.getHeaderName());
if (actualToken == null) {
actualToken = request.getParameter(csrfToken.getParameterName());
}
if (!equalsConstantTime(csrfToken.getToken(), actualToken)) {
AccessDeniedException exception = (!missingToken)
? new InvalidCsrfTokenException(csrfToken, actualToken)
: new MissingCsrfTokenException(actualToken);
this.accessDeniedHandler.handle(request, response, exception);
return;
}
filterChain.doFilter(request, response);
}
}
</code>Often developers disable CSRF for simplicity.
Disabling CSRF
<code>@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
// Disable CSRF filter
http.csrf().disable();
// Require authentication for any request
http.authorizeRequests().anyRequest().authenticated();
// Enable default login page
http.formLogin();
}
}
</code>Custom In‑Memory Authentication Configuration
<code>@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@SuppressWarnings("deprecation")
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// Define user, password and role
auth.inMemoryAuthentication()
.passwordEncoder(NoOpPasswordEncoder.getInstance())
.withUser("guest").password("123456").roles("ADMIN");
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
http.authorizeRequests().anyRequest().authenticated();
http.formLogin();
}
}
</code>After applying this configuration, authentication uses the defined in‑memory user.
The article ends here; the next part will cover request‑interception rules and custom login pages.
Spring Full-Stack Practical Cases
Full-stack Java development with Vue 2/3 front-end suite; hands-on examples and source code analysis for Spring, Spring Boot 2/3, and Spring Cloud.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.