Spring Security 6 【12-CSRF(跨站请求伪造)防护】

在 Spring Security 6 中,CSRF(跨站请求伪造)防护默认启用,以下是详细说明和代码示例:

核心概念

  1. CSRF 防护原理

    • 服务端生成唯一令牌(CSRF Token)
    • 客户端在修改状态的请求(POST/PUT/DELETE/PATCH)中携带令牌
    • 服务端验证令牌有效性
  2. 令牌存储方式

    • CookieCsrfTokenRepository:令牌存储在 XSRF-TOKEN Cookie 中(推荐)
    • HttpSessionCsrfTokenRepository:令牌存储在 Session 中

配置代码(Java Config)

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.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.csrf.CookieCsrfTokenRepository;
import org.springframework.security.web.csrf.CsrfTokenRequestAttributeHandler;

@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        // 自定义请求处理器(适配SPA应用)
        CsrfTokenRequestAttributeHandler requestHandler = new CsrfTokenRequestAttributeHandler();
        requestHandler.setCsrfRequestAttributeName(null); // 禁用从请求属性中获取token

        http
            // 配置CSRF(使用Cookie存储Token)
            .csrf(csrf -> csrf
                .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
                .csrfTokenRequestHandler(requestHandler)
            )
            // 授权配置
            .authorizeHttpRequests(auth -> auth
                .requestMatchers("/public/**").permitAll()
                .anyRequest().authenticated()
            )
            // 表单登录
            .formLogin(form -> form
                .loginPage("/login")
                .permitAll()
            );
            
        return http.build();
    }
}

客户端集成示例

1. Thymeleaf 表单(自动处理)
<form method="post" action="/transfer">
    <!-- 自动注入CSRF令牌 -->
    <input type="hidden" 
           th:name="${_csrf.parameterName}" 
           th:value="${_csrf.token}" />
    
    <!-- 表单字段 -->
    <input type="text" name="amount"/>
    <button type="submit">提交</button>
</form>
2. JavaScript(SPA 应用)
// 从Cookie中获取CSRF令牌
function getCsrfToken() {
    return document.cookie
        .split('; ')
        .find(row => row.startsWith('XSRF-TOKEN='))
        ?.split('=')[1];
}

// 发送请求时携带令牌
fetch('/api/transfer', {
    method: 'POST',
    headers: {
        'Content-Type': 'application/json',
        'X-XSRF-TOKEN': getCsrfToken() // 关键头
    },
    body: JSON.stringify({ amount: 100 })
});
3. 手动禁用CSRF(不推荐)
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
    http
        .csrf(csrf -> csrf.disable()) // 禁用CSRF防护
        // ...其他配置
    return http.build();
}

关键配置说明

配置项说明
CookieCsrfTokenRepository令牌通过Cookie传输(符合OAuth2标准)
withHttpOnlyFalse()允许JavaScript读取Cookie(SPA必需)
X-XSRF-TOKEN客户端需要在请求头中添加此令牌
CsrfTokenRequestAttributeHandler新处理器,解决CsrfToken在请求属性中的问题(Spring Security 6.1+)

自定义行为

排除特定路径
.csrf(csrf -> csrf
    .ignoringRequestMatchers("/api/public/**") // 忽略公共API
)
自定义令牌仓库
.csrf(csrf -> csrf
    .csrfTokenRepository(new CustomTokenRepository())
)

常见问题解决

  1. 403 Forbidden 错误

    • 检查客户端是否发送了有效的 X-XSRF-TOKEN
    • 确认Cookie域/路径配置正确
  2. Cookie 未生成

    • 确保首次访问安全端点(如登录页)
    • 检查浏览器是否阻止第三方Cookie
  3. SPA 应用适配

    • 使用 CookieCsrfTokenRepository.withHttpOnlyFalse()
    • 实现令牌从Cookie到请求头的自动转换

最佳实践:对浏览器访问保留CSRF防护,对非浏览器客户端(如移动APP)使用JWT等替代方案。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值