You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Spring Boot中正确实现CSRF防护?

CSRF防护配置问题排查与解决

初始问题

在为应用实现CSRF防护时遇到问题,当前安全配置如下:

@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
    return http
            .authorizeHttpRequests(authorization -> authorization
                    .requestMatchers("/api/admin/**").hasRole("ADMIN")
                    .requestMatchers("/api/moderator/**").hasAnyRole("ADMIN", "MODERATOR")
                    .requestMatchers("/api/orders/**").authenticated()
                    .requestMatchers("/api/**").permitAll()
                    .anyRequest().authenticated())
            .csrf().disable()
            .addFilterBefore(jwtFilter(), UsernamePasswordAuthenticationFilter.class)
            .build();
}

移除.csrf().disable()后,所有POST请求都会被拦截并返回403 Forbidden,需要明确如何安全实现CSRF防护,让服务器识别可信请求。

更新尝试后的问题

添加CSRF配置后,服务器会向Postman返回XSRF-TOKEN:

@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
    return http
            .authorizeHttpRequests(authorization -> authorization
                .requestMatchers("/api/admin/**").hasRole("ADMIN")
                .requestMatchers("/api/moderator/**").hasAnyRole("ADMIN", "MODERATOR")
                .requestMatchers("/api/orders/**").authenticated()
                .requestMatchers("/api/**").permitAll()
                .anyRequest().authenticated())
            .addFilterBefore(jwtFilter(), UsernamePasswordAuthenticationFilter.class)
            .csrf((csrf) -> csrf
                .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()))
            .build();
}

在Postman请求中添加X-XSRF-TOKEN请求头并填入服务器返回的值后,仍收到403错误,服务器日志显示:为http://localhost:8080/api/login找到无效的CSRF令牌


问题根源:JWT与CSRF的兼容性

你的应用同时使用JWT认证和CSRF防护,核心矛盾在于:Spring Security的CSRF防护默认依赖服务器端会话(Session),但JWT是无状态认证方案,不依赖服务器会话。这种情况下,CSRF令牌的存储与验证逻辑会出现不匹配,导致令牌无效的错误。

解决方案步骤

1. 按需配置CSRF(推荐方案)

对于使用JWT的无状态API接口,完全不需要启用CSRF防护——CSRF攻击的核心是利用浏览器自动携带会话Cookie的特性,而JWT通常放在Authorization: Bearer xxx请求头中,不会被浏览器自动携带,攻击者无法伪造带有效JWT的请求。

你可以针对非API的页面请求(如果存在)启用CSRF,对所有API接口禁用:

@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
    return http
            .authorizeHttpRequests(authorization -> authorization
                    .requestMatchers("/api/admin/**").hasRole("ADMIN")
                    .requestMatchers("/api/moderator/**").hasAnyRole("ADMIN", "MODERATOR")
                    .requestMatchers("/api/orders/**").authenticated()
                    .requestMatchers("/api/**").permitAll()
                    .anyRequest().authenticated())
            .csrf(csrf -> csrf
                    // 对所有/api开头的接口跳过CSRF验证
                    .ignoringRequestMatchers("/api/**")
                    // 对非API的页面请求启用CSRF,用Cookie存储令牌
                    .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()))
            .addFilterBefore(jwtFilter(), UsernamePasswordAuthenticationFilter.class)
            .build();
}

2. 若必须为API启用CSRF(不推荐)

如果场景特殊必须为API接口启用CSRF,需要确保以下几点:

  • 客户端先发送一次GET请求获取XSRF-TOKEN Cookie
  • 后续POST/PUT/DELETE请求需同时携带XSRF-TOKEN Cookie和X-XSRF-TOKEN请求头,且两者值完全一致
  • Postman配置注意事项:
    • 开启自动保存Cookie(在Settings -> Cookies中启用)
    • 获取Cookie后再发送POST请求,手动添加X-XSRF-TOKEN头,值直接取Cookie中的XSRF-TOKEN内容
    • 不要手动修改Cookie值,确保请求时Cookie自动携带到服务器

3. 检查过滤器顺序

确保JWT过滤器不会在CSRF过滤器之前执行——CSRF验证需要在认证前完成,否则服务器未识别用户就会拒绝请求。可调整过滤器顺序:

// 让JWT过滤器在CSRF过滤器之后执行
.addFilterAfter(jwtFilter(), CsrfFilter.class)

关键说明

  • 无状态JWT API无需CSRF防护,因为其认证凭证不会被浏览器自动携带,从根源上避免了CSRF攻击的可能。
  • 仅当应用使用传统Session认证(依赖浏览器会话Cookie)时,才需要启用CSRF防护,此时Cookie存储的CSRF令牌才能正常工作。

内容的提问来源于stack exchange,提问作者eskil4152

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.02 02:05:58