CSRF Token Generation in Spring Boot
CSRF Token Generation in Spring Boot
In Spring Boot (and Spring Security more generally), CSRF (Cross-Site Request
Forgery) tokens are generated and managed automatically by the framework
to protect against CSRF attacks.
When you include Spring Security in your Spring Boot project, CSRF protection
is enabled by default for web applications.
Spring Security generates a unique CSRF token for the user session.
UUID.randomUUID().toString();
Example:
@Bean
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.csrf(csrf -> csrf
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
);
return http.build();
}
4. CSRF Token in the HTML Page
If you use Thymeleaf or JSP, Spring can automatically include the CSRF token in
forms:
Example (Thymeleaf):
For JavaScript-based clients (e.g., Angular, React), the token can be retrieved
from a cookie or an endpoint and sent via header.
Spring Security checks for the CSRF token in the request (header or form
field).
You can disable CSRF protection (not recommended for browser-based clients):
Or use it selectively for specific endpoints (e.g., enable it for web, disable it for
APIs).
Customization Options
You can customize CSRF token behavior in Spring Security:
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http)
throws Exception {
http
.csrf(csrf -> csrf
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
// or use custom token repository
// .csrfTokenRepository(new CustomTokenRepository())
);
return http.build();
}
}
Example Flow
1. First GET request:
This mechanism ensures that only requests originating from your own site can
perform state-changing operations.
Setting up CSRF protection in a Spring Boot backend with an Angular
frontend:
@Configuration
@EnableWebSecurity
@Bean
http
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()
)
.requestMatchers("/api/**").authenticated()
.anyRequest().permitAll()
.formLogin(withDefaults())
.httpBasic(withDefaults());
return http.build();
@Bean
@Override
registry.addMapping("/**")
.allowedOrigins("http://localhost:4200")
.allowedMethods("*")
.allowCredentials(true);
};
// csrf.interceptor.ts
if (csrfToken) {
req = req.clone({
withCredentials: true,
setHeaders: {
'X-XSRF-TOKEN': csrfToken
});
return next.handle(req);
}
}
// app.module.ts
@NgModule({
providers: [
})
3. Service Example:
// data.service.ts
postData(data: any) {
withCredentials: true
});
Key Points:
1. Token Flow:
2. Important Headers:
Cookie: XSRF-TOKEN=abc123
X-XSRF-TOKEN: abc123
3. Security Considerations:
o Keep HttpOnly=false for the CSRF cookie (so Angular can read it)
@RestController
@GetMapping("/api/csrf")
}
@PostMapping("/api/test")
Verification Steps:
1. Check browser cookies after first GET request - should see XSRF-TOKEN
o X-XSRF-TOKEN header
java
CopyEdit
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import
org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.csrf.CookieCsrfTokenRepository;
@Configuration
@Bean
http
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()
)
)
.authorizeHttpRequests(auth -> auth
.requestMatchers("/api/**").permitAll()
.anyRequest().authenticated()
.httpBasic();
return http.build();
If you're serving the Angular frontend through Spring Boot, ensure that your
controller maps to the appropriate route:
java
CopyEdit
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
@Controller
return "index.html";
Create an HTTP interceptor to read the CSRF token from the cookie and include
it in the headers of HTTP requests:
typescript
CopyEdit
// csrf.interceptor.ts
@Injectable()
if (csrfToken) {
req = req.clone({
setHeaders: {
'X-XSRF-TOKEN': csrfToken
});
return next.handle(req);
return null;
typescript
CopyEdit
@NgModule({
...
providers: [
})
With the interceptor in place, you can make HTTP requests as usual, and the
CSRF token will be automatically included:
typescript
CopyEdit
console.log('Saved:', response);
});
🔄 Workflow Summary
4. Token Validation: Spring Security validates the CSRF token from the
header against the one stored in the cookie. If they match, the request is
processed; otherwise, it's rejected.
📚 Reference Implementation
For a practical example, you can refer to the following GitHub repository:
java
Copy
Download
@Configuration
@EnableWebSecurity
@Bean
http
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()
)
.requestMatchers("/api/**").authenticated()
.anyRequest().permitAll()
)
.formLogin(withDefaults())
.httpBasic(withDefaults());
return http.build();
java
Copy
Download
@Bean
@Override
registry.addMapping("/**")
.allowedOrigins("http://localhost:4200")
.allowedMethods("*")
.allowCredentials(true);
};
typescript
Copy
Download
// csrf.interceptor.ts
if (csrfToken) {
req = req.clone({
withCredentials: true,
setHeaders: {
'X-XSRF-TOKEN': csrfToken
});
}
}
return next.handle(req);
typescript
Copy
Download
// app.module.ts
@NgModule({
providers: [
})
export class AppModule {}
3. Service Example:
typescript
Copy
Download
// data.service.ts
postData(data: any) {
withCredentials: true
});
Key Points:
1. Token Flow:
Copy
Download
Cookie: XSRF-TOKEN=abc123
X-XSRF-TOKEN: abc123
3. Security Considerations:
o Keep HttpOnly=false for the CSRF cookie (so Angular can read it)
java
Copy
Download
@RestController
@GetMapping("/api/csrf")
@PostMapping("/api/test")
Verification Steps:
1. Check browser cookies after first GET request - should see XSRF-TOKEN
o X-XSRF-TOKEN header
I'll outline a complete GitHub repository structure for a Spring Boot + Angular
application with CSRF protection. You can find a ready-to-use implementation
here:
GitHub Repository:
🔗 https://github.com/example/spring-boot-angular-csrf-demo (Note: This is a
placeholder link - see below for actual implementation steps)
Repository Structure
Copy
Download
spring-boot-angular-csrf-demo/
│ ├── src/
│ │ ├── main/
│ │ │ ├── java/com/example/demo/
│ │ │ │ ├── config/
│ │ │ │ │ └── SecurityConfig.java
│ │ │ │ ├── controller/
│ │ │ │ │ └── ApiController.java
│ │ │ │ └── DemoApplication.java
│ │ │ └── resources/
│ │ │ └── application.properties
│ │ └── test/
│ └── pom.xml
│ ├── src/
│ │ ├── app/
│ │ │ ├── services/
│ │ │ │ └── api.service.ts
│ │ │ ├── interceptors/
│ │ │ │ └── csrf.interceptor.ts
│ │ │ └── app.module.ts
│ │ ├── assets/
│ │ ├── environments/
│ │ └── index.html
│ ├── angular.json
│ └── package.json
└── README.md
bash
Copy
Download
2. Frontend (Angular):
bash
Copy
Download
ng new frontend
cd frontend
java
Copy
Download
@Configuration
@EnableWebSecurity
@Bean
http
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()
)
.ignoringRequestMatchers("/api/public/**")
.requestMatchers("/api/public/**").permitAll()
.anyRequest().authenticated()
@Bean
CorsConfigurationSource corsConfigurationSource() {
configuration.setAllowedOrigins(Arrays.asList("http://localhost:4200"));
configuration.setAllowedMethods(Arrays.asList("GET","POST","PUT","DELETE"));
configuration.setAllowCredentials(true);
configuration.addExposedHeader("X-XSRF-TOKEN");
source.registerCorsConfiguration("/**", configuration);
return source;
2. Angular CSRF
Interceptor (frontend/src/app/interceptors/csrf.interceptor.ts):
typescript
Copy
Download
import {
HttpRequest,
HttpHandler,
HttpEvent,
HttpInterceptor
} from '@angular/common/http';
@Injectable()
request = request.clone({
withCredentials: true,
setHeaders: {
'X-XSRF-TOKEN': token
});
}
return next.handle(request);
bash
Copy
Download
# In backend directory
./mvnw spring-boot:run
# In frontend directory
ng serve