Spring Security CSRF防护与CORS配置实战指南
2026/7/22 13:45:55 网站建设 项目流程

1. Spring Security中的CSRF防护机制解析

CSRF(Cross-Site Request Forgery)跨站请求伪造是Web应用中常见的安全威胁。Spring Security默认会对所有非安全HTTP方法(如POST、PUT等)启用CSRF防护。其核心原理是通过同步器令牌模式:

  • 服务端生成唯一令牌(CsrfToken)
  • 令牌存储在会话或Cookie中
  • 客户端需在后续请求中携带该令牌
  • 服务端验证令牌有效性

1.1 默认配置与工作原理

Spring Security 6.x的默认CSRF配置包含以下关键组件:

@Configuration @EnableWebSecurity public class SecurityConfig { @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http .csrf(Customizer.withDefaults()); // 启用默认CSRF防护 return http.build(); } }

默认实现包含三个核心部分:

  1. HttpSessionCsrfTokenRepository- 将令牌存储在HttpSession中
  2. XorCsrfTokenRequestAttributeHandler- 提供BREACH攻击防护
  3. CsrfFilter- 处理实际的令牌验证逻辑

关键点:从Spring Security 6开始,CSRF令牌加载改为延迟模式,只有需要验证的请求才会加载会话,这显著提升了性能。

1.2 令牌存储策略对比

Spring Security提供两种主要的令牌存储方式:

存储方式实现类适用场景优点缺点
Session存储HttpSessionCsrfTokenRepository传统Web应用安全性高需要会话支持
Cookie存储CookieCsrfTokenRepositorySPA/前后端分离无状态需防范XSS

Cookie存储的典型配置:

http.csrf(csrf -> csrf .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()) );

1.3 禁用CSRF防护的场景

在某些API-only的应用中可能需要禁用CSRF:

http.csrf(csrf -> csrf.disable());

更精细的控制方式是指定忽略路径:

http.csrf(csrf -> csrf .ignoringRequestMatchers("/api/public/**") );

2. CORS跨域配置实战

2.1 CORS与CSRF的关系

虽然都涉及跨域安全,但CORS和CSRF解决的是不同层面的问题:

  • CORS:控制哪些外部域可以访问资源
  • CSRF:防止未授权的命令执行

2.2 Spring Security中的CORS配置

全局CORS配置示例:

@Bean public CorsConfigurationSource corsConfigurationSource() { CorsConfiguration config = new CorsConfiguration(); config.setAllowedOrigins(List.of("https://trusted.com")); config.setAllowedMethods(List.of("GET","POST")); config.setAllowCredentials(true); UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); source.registerCorsConfiguration("/**", config); return source; }

与Spring Security集成:

http.cors(cors -> cors .configurationSource(corsConfigurationSource()) );

2.3 常见CORS错误处理

遇到"has been blocked by CORS policy"错误时检查:

  1. 预检请求(OPTIONS)是否通过
  2. 响应头是否包含Access-Control-Allow-Origin
  3. 带凭证请求时是否设置allowCredentials=true
  4. 复杂请求是否声明了Access-Control-Allow-Methods

3. Session管理高级配置

3.1 会话固定保护

Spring Security默认启用会话固定保护:

http.sessionManagement(session -> session .sessionFixation().migrateSession() );

可选策略:

  • changeSessionId:仅更改ID(Servlet 3.1+)
  • migrateSession:创建新会话并复制属性(默认)
  • newSession:创建全新会话
  • none:禁用保护

3.2 并发会话控制

限制单个用户的会话数量:

http.sessionManagement(session -> session .maximumSessions(1) .maxSessionsPreventsLogin(true) );

3.3 会话超时处理

结合CSRF时需特别注意:

@Bean public HttpSessionEventPublisher httpSessionEventPublisher() { return new HttpSessionEventPublisher(); }

配置会话失效URL:

http.sessionManagement(session -> session .invalidSessionUrl("/session-expired") );

4. 综合配置示例

4.1 安全配置模板

@Configuration @EnableWebSecurity public class SecurityConfig { @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http .cors(cors -> cors .configurationSource(corsConfigurationSource()) ) .csrf(csrf -> csrf .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()) .ignoringRequestMatchers("/api/public/**") ) .sessionManagement(session -> session .sessionFixation().migrateSession() .maximumSessions(1) .expiredUrl("/session-expired") ); return http.build(); } @Bean public CorsConfigurationSource corsConfigurationSource() { // 同上文CORS配置 } }

4.2 测试配置要点

测试CSRF防护的MockMvc示例:

@Autowired private WebApplicationContext context; private MockMvc mockMvc; @BeforeEach void setup() { mockMvc = MockMvcBuilders .webAppContextSetup(context) .apply(springSecurity()) .build(); } @Test void postWithValidCsrf() throws Exception { mockMvc.perform(post("/submit") .with(csrf())) // 自动添加有效CSRF令牌 .andExpect(status().isOk()); }

5. 疑难问题排查指南

5.1 CSRF相关错误

问题1:InvalidCsrfTokenException

可能原因:

  • 表单缺少_csrf参数
  • AJAX请求未携带X-CSRF-TOKEN头
  • 会话超时导致令牌失效

解决方案:

  • 检查表单是否包含:
    <input type="hidden" name="_csrf" th:value="${_csrf.token}"/>
  • 对于AJAX请求,添加:
    headers: {'X-CSRF-TOKEN': token}

问题2:CookieCsrfTokenRepository不工作

检查点:

  1. 是否设置了withHttpOnlyFalse()
  2. 前端是否正确读取XSRF-TOKEN cookie
  3. 是否在请求头中携带X-XSRF-TOKEN

5.2 CORS相关错误

问题:预检请求失败

典型表现:

Response to preflight request doesn't pass access control check

解决方案:

  1. 确保OPTIONS请求不被拦截:
    .requestMatchers(HttpMethod.OPTIONS).permitAll()
  2. 检查CORS配置是否包含所需方法:
    config.setAllowedMethods(List.of("GET","POST","PUT","DELETE","OPTIONS"));

5.3 Session相关错误

问题:session persistence异常

现象:

  • 重启后会话丢失
  • 集群环境下会话不同步

解决方案:

  1. 配置持久化Session存储:
    spring.session.store-type=redis
  2. 添加依赖:
    <dependency> <groupId>org.springframework.session</groupId> <artifactId>spring-session-data-redis</artifactId> </dependency>

6. 性能优化建议

  1. CSRF令牌延迟加载(Spring Security 6+默认)

    // 无需特别配置,默认已启用
  2. 会话优化

    • 减少会话中存储的数据量
    • 考虑使用session.stateless=true的无状态架构
  3. CORS缓存

    config.setMaxAge(3600L); // 预检结果缓存1小时
  4. 异步处理

    http.sessionManagement(session -> session .sessionCreationPolicy(SessionCreationPolicy.ALWAYS) );

实际项目中,我曾遇到一个SPA应用因频繁预检请求导致性能下降的情况。通过调整CORS缓存时间和精简允许的方法列表,最终将API响应时间降低了40%。关键配置如下:

config.setAllowedMethods(List.of("GET","POST")); // 仅允许必要方法 config.setMaxAge(7200L); // 预检缓存2小时

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询