摘要:在当今互联网软件开发领域,安全问题始终是重中之重。随着应用程序复杂度的不断提升以及网络攻击手段的日益多样化,保障应用的安全性成为每一位开发者的核心任务。其中,CSRF(Cross - Site Request Forgery,跨站请求伪造)攻击作为一种常见且极
在当今互联网软件开发领域,安全问题始终是重中之重。随着应用程序复杂度的不断提升以及网络攻击手段的日益多样化,保障应用的安全性成为每一位开发者的核心任务。其中,CSRF(Cross - Site Request Forgery,跨站请求伪造)攻击作为一种常见且极具威胁性的网络攻击方式,对基于 Spring Boot3 开发的应用程序构成了严峻挑战。本文将深入探讨 Spring Boot3 中防御 CSRF 攻击的相关知识与实践,助力开发者构建更加安全可靠的软件系统。
CSRF 攻击利用了用户在已登录 Web 应用程序时的身份验证状态,在用户不知情的情况下,诱使用户执行非本意的操作。其攻击流程大致如下:
用户登录:用户成功登录到目标 Web 应用 A,浏览器会存储与该应用相关的身份验证凭据,常见的如 Cookie、Session 等。此时,用户在应用 A 中处于已认证状态,后续的请求会携带这些凭据,服务器根据这些凭据识别用户身并处理请求。
恶意请求引入:用户在保持应用 A 登录状态的同时,访问了恶意网站 B。网站 B 中隐藏着针对应用 A 的恶意请求,这些请求可能以隐藏表单、JavaScript 脚本等形式存在。
自动提交恶意请求:当用户访问恶意网站 B 时,由于浏览器会自动发送与目标应用 A 相关的身份验证凭据(如 Cookie),恶意请求会携带这些凭据被发送到应用 A 的服务器。服务器接收到请求后,根据凭据认为该请求来自合法用户,进而执行恶意请求所指定的操作,例如转账、修改密码、发布恶意内容等。
例如,用户在登录网上银行应用 A 后,未退出登录的情况下又访问了一个看似普通的图片网站 B。而网站 B 被攻击者植入了恶意代码,该代码会自动向银行应用 A 发送转账请求,由于请求携带了用户在银行应用 A 的有效 Cookie,银行服务器会误认为这是用户本人的合法操作,从而执行转账,导致用户资金受损。
从攻击原理可以看出,CSRF 攻击的关键在于利用了用户身份验证凭据的自动发送机制,以及服务器对请求来源的信任判断漏洞。因此,防御 CSRF 攻击的核心思路就是打破攻击者利用这种机制的可能性,确保服务器能够准确识别并拒绝恶意请求。
Spring Boot3 框架集成了强大的 Spring Security 模块,为开发者提供了一系列默认的 CSRF 防护机制。在默认情况下,对于常见的 Web 应用场景,Spring Security 会自动启用 CSRF 保护,尤其是针对会话管理的应用。其核心防护机制主要基于 CSRF 令牌(CSRF Token)。
(一)CSRF 令牌生成与存储
生成过程:当用户与应用程序建立会话(例如登录操作)时,Spring Security 会为该用户会话生成一个唯一且不可预测的 CSRF 令牌。这个令牌是一串随机生成的字符序列,具有高度的随机性和唯一性,极大地增加了攻击者猜测或伪造令牌的难度。
存储方式:
Cookie 存储:Spring Security 默认将 CSRF 令牌存储在名为 “XSRF - TOKEN” 的 Cookie 中。这种方式使得令牌可以在客户端(浏览器)与服务器之间方便地传递。在后续用户发起请求时,浏览器会自动携带该 Cookie,服务器可以从中提取令牌进行验证。需要注意的是,默认情况下,该 Cookie 的 “HttpOnly” 属性被设置为 false,这意味着 JavaScript 脚本可以读取该 Cookie 的值,以便在 AJAX 请求等场景中使用。然而,这也带来了一定的安全风险,如果 JavaScript 代码存在漏洞,可能会导致令牌泄露。在一些对安全性要求极高的场景下,可以考虑将 “HttpOnly” 属性设置为 true,但这会增加在某些前端场景下获取令牌的难度。
Session 存储:除了 Cookie,CSRF 令牌也会同时存储在服务器端的用户会话(Session)中。这为服务器提供了一个可靠的令牌验证参考源。当服务器接收到请求时,会分别从请求中提取令牌(可能来自请求参数或请求头)以及从用户会话中获取存储的令牌进行比对,以验证请求的合法性。
(二)CSRF 令牌在请求中的使用
表单提交场景:在传统的 HTML 表单提交场景中,如果使用的是 Thymeleaf 等模板引擎,Spring Security 会自动在表单中添加一个隐藏字段,字段名为 “_csrf”,其值即为当前用户会话的 CSRF 令牌。例如:
提交当用户提交表单时,这个隐藏的 CSRF 令牌会随着表单数据一起被发送到服务器。服务器在接收到表单提交请求后,会从请求参数中提取 “_csrf” 字段的值,并与存储在用户会话中的 CSRF 令牌进行比对。如果两者一致,则认为该请求是合法的,继续处理请求;如果不一致,服务器会拒绝该请求,并返回相应的错误信息,通常是 HTTP 状态码 403(禁止访问)。
AJAX 请求场景:对于通过 AJAX 进行的异步请求,由于没有像传统表单那样的自动添加 CSRF 令牌的机制,开发者需要手动将 CSRF 令牌添加到请求中。一种常见的做法是从页面中获取存储在 meta 标签中的 CSRF 令牌信息,然后在发送 AJAX 请求时将其添加到请求头中。例如,在 HTML 页面中,可以通过以下方式获取 CSRF 令牌相关信息:
然后,在 JavaScript 代码中发送 AJAX 请求时,添加 CSRF 令牌到请求头:
const csrfToken = document.querySelector('meta[name="_csrf"]').getAttribute('content');const csrfHeader = document.querySelector('meta[name="_csrf_header"]').getAttribute('content');fetch('/ajax - endpoint', {method: 'POST',headers: {'Content - Type': 'application/json',[csrfHeader]: csrfToken},body: JSON.stringify({ data: 'example' })});这样,服务器在接收到 AJAX 请求时,能够从请求头中提取 CSRF 令牌并进行验证,确保 AJAX 请求的合法性。
(一)启用与禁用 CSRF 防护
启用 CSRF 防护:在 Spring Boot3 项目中,如果要使用 Spring Security 的 CSRF 防护功能,首先需要确保项目中引入了 Spring Security 相关依赖。在 Maven 项目中,可以在pom.xml文件中添加如下依赖:
org.springframework.bootspring - boot - starter - security引入依赖后,Spring Security 默认会启用 CSRF 防护功能。如果需要显式配置启用 CSRF 防护,可以在 Spring Security 配置类中进行如下设置:
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.config.annotation.web.configuration.WebSecurityConfigurerAdapter;@Configuration@EnableWebSecuritypublic class SecurityConfig extends WebSecurityConfigurerAdapter {@Overrideprotected void configure(HttpSecurity http) throws Exception {http.csrf(csrf -> csrf.enable).authorizeRequests(auth -> auth.requestMatchers("/").permitAll.anyRequest.authenticated).formLogin;}}上述代码中,http.csrf(csrf -> csrf.enable)显式启用了 CSRF 防护功能。同时,配置了访问权限,根路径 “/” 允许所有用户访问,其他请求需要用户进行身份认证。
禁用 CSRF 防护:在某些特定场景下,例如应用程序以无状态形式运行,如 REST API,可能需要禁用 CSRF 防护。因为在无状态的 RESTful 架构中,通常不依赖于会话管理,CSRF 攻击的风险相对较低,并且 CSRF 防护机制可能会对一些简单的 API 调用造成不必要的阻碍。要禁用 CSRF 防护,可以在 Spring Security 配置类中进行如下修改:
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.config.annotation.web.configuration.WebSecurityConfigurerAdapter;@Configuration@EnableWebSecuritypublic class SecurityConfig extends WebSecurityConfigurerAdapter {@Overrideprotected void configure(HttpSecurity http) throws Exception {http.csrf(csrf -> csrf.disable).authorizeRequests(auth -> auth.requestMatchers("/").permitAll.anyRequest.authenticated).formLogin;}}这里通过http.csrf(csrf -> csrf.disable)禁用了 CSRF 防护功能。需要注意的是,禁用 CSRF 防护后,应确保通过其他方式(如严格的身份验证、签名机制等)来保障应用程序的安全性,防止潜在的攻击风险。
(二)自定义 CSRF 防护规则
指定需要 CSRF 保护的请求路径或方法:在实际应用中,可能并非所有的请求都需要 CSRF 保护,或者需要对特定的请求路径或方法进行更细致的 CSRF 防护设置。Spring Security 允许开发者通过自定义requireCsrfProtectionMatcher来实现这一需求。例如,只对所有的 POST 请求启用 CSRF 保护,可以在 Spring Security 配置类中进行如下配置:
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.config.annotation.web.configuration.WebSecurityConfigurerAdapter;@Configuration@EnableWebSecuritypublic class SecurityConfig extends WebSecurityConfigurerAdapter {@Overrideprotected void configure(HttpSecurity http) throws Exception {http.csrf(csrf -> csrf.requireCsrfProtectionMatcher(request -> {return request.getMethod.equals("POST");})).authorizeRequests(auth -> auth.requestMatchers("/").permitAll.anyRequest.authenticated).formLogin;}}上述代码中,requireCsrfProtectionMatcher方法接收一个RequestMatcher对象,通过判断请求的方法是否为 “POST” 来决定是否对该请求启用 CSRF 保护。
排除特定路径的 CSRF 防护:有时,应用程序中存在一些公开的 API 接口或特定的路径,这些路径不需要 CSRF 防护,并且如果启用 CSRF 防护可能会导致一些问题。此时,可以通过ignoringAntMatchers方法来排除这些特定路径。例如,假设存在一个公开的 API 路径 “/api/public”,需要排除该路径的 CSRF 防护,可以进行如下配置:
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.config.annotation.web.configuration.WebSecurityConfigurerAdapter;@Configuration@EnableWebSecuritypublic class SecurityConfig extends WebSecurityConfigurerAdapter {@Overrideprotected void configure(HttpSecurity http) throws Exception {http.csrf(csrf -> csrf.ignoringAntMatchers("/api/public")).authorizeRequests(auth -> auth.requestMatchers("/").permitAll.anyRequest.authenticated).formLogin;}}这样,所有对 “/api/public” 路径的请求将不会受到 CSRF 防护机制的检查。在使用此功能时,要确保排除的路径确实不存在 CSRF 攻击风险,并且已经通过其他方式(如 API 密钥验证、IP 白名单等)进行了充分的安全保护。
(三)CSRF 防御的测试与验证
正常请求测试:为了验证 CSRF 防御机制在正常情况下的工作情况,可以通过编写测试用例来模拟用户的正常请求。例如,在一个 Spring Boot 项目中,可以使用 Spring Security 提供的测试工具MockMvc来测试表单提交和 AJAX 请求。假设存在一个处理用户注册的表单,表单提交路径为 “/register”,可以编写如下测试用例:
import org.junit.jupiter.api.Test;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfiguremockMvc;import org.springframework.boot.test.context.SpringBootTest;import org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors;import org.springframework.test.web.servlet.MockMvc;import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;@SpringBootTest@AutoConfigureMockMvcpublic class CsrfTest {@Autowiredprivate MockMvc mockMvc;@Testpublic void testFormSubmissionWithCsrf throws Exception {mockMvc.perform(post("/register").with(SecurityMockMvcRequestPostProcessors.csrf).param("username", "testUser").param("password", "testPassword")).andExpect(status.isOk);}}在上述测试用例中,with(SecurityMockMvcRequestPostProcessors.csrf)方法会自动在请求中添加 CSRF 令牌,模拟正常用户的表单提交请求。如果测试通过,说明 CSRF 防御机制在正常表单提交场景下能够正确工作,服务器能够接受并处理携带有效 CSRF 令牌的请求。
对于 AJAX 请求的测试,可以类似地编写测试用例,确保在请求头中正确添加 CSRF 令牌后,服务器能够正常响应请求。
伪造请求测试:为了验证 CSRF 防御机制是否能够有效拦截伪造的请求,需要模拟攻击者发送不携带 CSRF 令牌或携带无效 CSRF 令牌的请求。继续以上述用户注册为例,可以编写如下测试用例:
import org.junit.jupiter.api.Test;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;import org.springframework.boot.test.context.SpringBootTest;import org.springframework.test.web.servlet.MockMvc;import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;@SpringBootTest@AutoConfigureMockMvcpublic class CsrfTest {@Autowiredprivate MockMvc mockMvc;@Testpublic void testForgedRequest throws Exception {mockMvc.perform(post("/register").param("username", "testUser").param("password", "testPassword")).andExpect(status.isForbidden);}}在这个测试用例中,请求没有携带 CSRF 令牌。根据 CSRF 防御机制,服务器应该拒绝该请求,并返回 HTTP 状态码 403(禁止访问)。如果测试结果符合预期,说明 CSRF 防御机制能够有效识别并拦截伪造的请求,保障了应用程序的安全性。
通过以上对正常请求和伪造请求的测试,可以全面验证 Spring Boot3 应用中 CSRF 防御机制的有效性,确保在各种场景下应用程序都能够抵御 CSRF 攻击。
(一)全面覆盖关键业务操作
在应用程序开发过程中,要确保对所有涉及用户关键业务操作的请求都进行 CSRF 防护。这些关键业务操作包括但不限于用户账户信息修改(如密码修改、邮箱绑定等)、资金交易(如转账、支付等)、敏感数据的创建与删除(如发布重要文章、删除企业核心数据等)。对于这些操作,如果没有 CSRF 防护,一旦遭受攻击,可能会给用户或企业带来严重的损失。因此,在设计和实现应用功能时,要明确区分哪些操作属于关键业务操作,并针对性地应用 CSRF 防护机制。
(二)定期更新与轮换 CSRF 令牌
为了进一步降低 CSRF 令牌被泄露或猜测的风险,建议定期更新和轮换 CSRF 令牌。Spring Security 默认的 CSRF 令牌生成机制虽然保证了令牌的随机性和唯一性,但随着时间的推移,令牌被攻击者获取的可能性会增加。通过定期轮换令牌,可以缩短攻击者利用泄露令牌进行攻击的有效时间窗口。在实际实现中,可以根据应用程序的具体需求和用户会话的活跃情况,设置合适的令牌轮换周期。例如,可以在用户每次进行关键业务操作时,或者每隔一定时间间隔(如 30 分钟),为用户重新生成一个新的 CSRF 令牌,并更新存储在 Cookie 和 Session 中的令牌值。同时,要确保在令牌轮换过程中,前端页面和请求逻辑能够正确获取和使用新的令牌,避免因令牌更新导致用户正常操作失败。
(三)结合其他安全机制
CSRF 防御只是应用程序安全防护体系的一部分,不能仅仅依赖 CSRF 防护来保障应用的整体安全。在实际开发中,应结合其他安全机制,构建一个全面、多层次的安全防护体系。例如,加强用户身份验证机制,采用多因素认证(MFA),除了用户名和密码外,还可以通过短信验证码、指纹识别、硬件令牌等方式进一步验证用户身份,确保只有合法用户能够访问应用程序。同时,对所有输入数据进行严格的验证和过滤,防止 SQL 注入、XSS(跨站脚本攻击)等其他类型的攻击。此外,合理设置访问控制策略,对不同用户角色分配明确的操作权限,遵循 “最小权限原则”—— 即仅授予用户完成其工作必需的权限,避免普通用户拥有敏感操作(如数据删除、系统配置修改)的权限。例如,在 Spring Boot3 中可通过@PreAuthorize注解或SecurityConfig配置角色权限:
// 方法级权限控制示例@RestController@RequestMapping("/admin")public class AdminController {@PreAuthorize("hasRole('ADMIN')") // 仅管理员可访问@PostMapping("/delete-data")public String deleteSensitiveData(@RequestParam String dataId) {// 数据删除逻辑return "数据删除成功";}}// 全局权限配置示例@Configuration@EnableWebSecurity@EnableGlobalMethodSecurity(prePostEnabled = true) // 启用方法级权限public class SecurityConfig extends WebSecurityConfigurerAdapter {@Overrideprotected void configure(HttpSecurity http) throws Exception {http.authorizeRequests(auth -> auth.antMatchers("/admin/**").hasRole("ADMIN").antMatchers("/user/**").hasAnyRole("ADMIN", "USER").anyRequest.authenticated);}}此外,还需定期对应用程序进行安全审计与漏洞扫描,借助 Spring Boot Actuator 暴露健康检查与审计端点,结合 SonarQube、OWASP ZAP 等工具检测 CSRF 防护配置是否存在漏洞,及时修复潜在风险。
(四)CSRF 防御的常见误区
在 Spring Boot3 项目开发中,开发者常因对 CSRF 机制理解不深入陷入误区,导致防护失效或影响业务正常运行,需重点规避以下问题:
误认为 “所有无状态接口都无需 CSRF 防护”
部分开发者认为 REST API 使用 JWT 令牌(无状态)时,无需启用 CSRF 防护,但实际需结合令牌存储方式判断:若 JWT 令牌存储在 Cookie 中(即使是自定义 Cookie),浏览器仍会自动携带,存在 CSRF 风险;若令牌存储在 LocalStorage 或请求头中(需手动携带),则风险较低。正确做法是:
当 JWT 存储在 Cookie 时,必须启用 CSRF 防护;当 JWT 通过请求头(如Authorization: Bearer {token})传递时,可禁用 CSRF,但需确保令牌不被 XSS 攻击窃取(如对前端脚本进行严格过滤)。过度排除 CSRF 防护路径
部分项目为图便捷,通过ignoringAntMatchers("/api/**")排除所有 API 路径的 CSRF 防护,导致包含用户身份验证的接口(如/api/user/update-password)暴露风险。正确做法是:仅排除完全公开、无需身份验证的接口(如首页数据查询、公开文档接口),且需在代码注释中明确排除原因,避免后续维护时误扩排除范围。
忽略前端框架的 CSRF 适配差异
不同前端框架(Vue、React、Angular)对 CSRF 令牌的传递方式存在差异,若直接照搬传统 HTML 表单的防护逻辑,易导致 AJAX 请求被拦截。例如:
// Vue + axios 全局拦截器配置import axios from 'axios';const csrfToken = document.cookie.split('; ').find(row => row.startsWith('XSRF-TOKEN='))?.split('=')[1]; // 从Cookie提取XSRF-TOKENaxios.interceptors.request.use(config => {if (csrfToken) {config.headers['X-XSRF-TOKEN'] = csrfToken; // 适配Spring Security默认头}return config;});Vue 项目中可通过 axios 拦截器统一添加 CSRF 令牌到请求头:React 项目中可借助fetch封装工具类,确保所有 POST/PUT 请求携带令牌,避免因框架特性导致令牌丢失。(五)不同场景下的 CSRF 防御适配策略
Spring Boot3 应用的部署场景(如单体应用、微服务、前后端分离)不同,CSRF 防御方案需灵活调整,以下为典型场景的适配方案:
1. 单体应用(传统前后端不分离)
此类应用多使用 Thymeleaf、Freemarker 等模板引擎,可充分利用 Spring Security 的自动配置:
表单提交:依赖模板引擎自动注入_csrf隐藏字段,无需手动处理;局部 AJAX 请求:通过页面meta标签或 Cookie 提取令牌,如前文 “AJAX 请求场景” 示例;注意事项:避免在静态 HTML 文件(非模板渲染)中写死表单,否则无法自动注入令牌,需手动通过后端接口获取令牌后渲染页面。2. 前后端分离应用(Vue/React + Spring Boot3)
前后端分离场景下,前端独立部署(如 Nginx),后端仅提供 API,需重点解决 “令牌传递” 与 “跨域” 的协同问题:
跨域配置与 CSRF 协同:
若前端与后端存在跨域(如前端http://localhost:8080,后端http://localhost:8081),需在后端配置跨域允许携带 Cookie(因 CSRF 令牌存储在 Cookie 中),同时指定允许的前端域名,避免恶意网站伪造请求:
@Configurationpublic class CorsConfig implements WebMvcConfigurer {@Overridepublic void addCorsMappings(CorsRegistry registry) {registry.addMapping("/api/**").allowedOrigins("http://localhost:8080") // 仅允许前端域名.allowedMethods("GET", "POST", "PUT", "DELETE").allowCredentials(true) // 允许携带Cookie.maxAge(3600); // 预检请求缓存时间}}令牌传递优化:
前端可在登录成功后,从响应 Cookie 中提取XSRF-TOKEN,存储到localStorage(需防范 XSS)或内存中,后续所有非 GET 请求统一在请求头携带该令牌,避免每次请求都从 Cookie 中解析。
3. 微服务架构(Spring Cloud + Spring Boot3)
微服务场景下,存在多个服务间调用、网关路由等情况,需确保 CSRF 防护在 “网关 - 服务” 链路中不失效:
网关层统一处理 CSRF:
若使用 Spring Cloud Gateway 作为网关,可在网关层配置 CSRF 令牌生成与验证,避免每个微服务重复配置。例如,通过网关过滤器提取请求中的令牌,验证通过后再路由到具体服务:
@Componentpublic class CsrfGatewayFilter implements GlobalFilter, Ordered {@Overridepublic Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) {ServerHttpRequest request = exchange.getRequest;// 非GET请求需验证CSRF令牌if (!"GET".equals(request.getMethodValue)) {String csrfToken = request.getHeaders.getFirst("X-XSRF-TOKEN");String storedToken = extractTokenFromSession(exchange); // 从分布式会话(如Redis)获取存储的令牌if (csrfToken == null || !csrfToken.equals(storedToken)) {exchange.getResponse.setStatusCode(HttpStatus.FORBIDDEN);return exchange.getResponse.setComplete; // 拦截非法请求}}return chain.filter(exchange);}@Overridepublic int getOrder {return Ordered.HIGHEST_PRECEDENCE; // 确保优先执行CSRF验证}}分布式会话适配:
微服务中用户会话通常存储在 Redis 等分布式存储中,需确保 CSRF 令牌与用户会话绑定 —— 即令牌生成后存储到 Redis(键为sessionId:csrfToken),验证时从 Redis 中获取对应会话的令牌,避免因服务集群部署导致令牌存储在单个服务内存中,引发跨节点验证失败。
即使配置了 CSRF 防护,仍可能因配置疏漏或环境差异导致防护失效,需掌握效果验证方法与常见故障排查思路:
(一)实战验证方法
Postman 手动测试
步骤 1:发送登录请求,获取响应 Cookie 中的XSRF-TOKEN;步骤 2:发送 POST 请求(如/user/update),不携带X-XSRF-TOKEN请求头,观察是否返回 403 状态码;步骤 3:在请求头中添加X-XSRF-TOKEN: {获取的令牌值},再次发送请求,观察是否返回 200 状态码(正常响应)。通过对比两次请求结果,验证防护是否生效。
浏览器控制台调试
在浏览器开发者工具(F12)的 “Network” 面板中,查看 POST 请求的请求头与响应头:
正常情况:请求头包含X-XSRF-TOKEN,响应状态码为 200;异常情况:无令牌时响应 403,需检查前端是否正确注入令牌;若有令牌仍 403,需排查后端令牌存储与验证逻辑(如 Redis 会话是否正常、令牌是否过期)。(二)常见故障排查方向
“有令牌仍返回 403” 故障
@Configurationpublic class SecurityConfig extends WebSecurityConfigurerAdapter {@Overrideprotected void configure(HttpSecurity http) throws Exception {http.csrf(csrf -> csrf.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse).csrfRequestMatcher(new CsrfRequestMatcher) // 自定义请求匹配器.csrfTokenRequestHandler(new XorCsrfTokenRequestAttributeHandler).requireCsrfProtectionMatcher(request -> !"GET".equals(request.getMethod))// 配置自定义请求头名称.and.addFilterAfter(new CsrfHeaderFilter, CsrfFilter.class));}}// 自定义过滤器:支持前端自定义的CSRF请求头public class CsrfHeaderFilter extends OncePerRequestFilter {@Overrideprotected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {CsrfToken csrf = (CsrfToken) request.getAttribute(CsrfToken.class.getName);if (csrf != null) {response.setHeader("X-CSRF-HEADER", csrf.getHeaderName);response.setHeader("X-CSRF-PARAM", csrf.getParameterName);response.setHeader("X-CSRF-TOKEN", csrf.getToken);}filterChain.doFilter(request, response);}}排查 1:令牌是否与用户会话匹配 —— 检查后端是否将令牌与sessionId绑定,避免不同用户的令牌混用;排查 2:令牌是否过期 —— 若配置了令牌过期时间(如 30 分钟),需检查前端是否在令牌过期后重新获取(如监听 403 响应,触发重新登录获取新令牌);排查 3:请求头名称是否匹配 ——Spring Security 默认接收X-XSRF-TOKEN或X-CSRF-TOKEN请求头,若前端自定义了头名称,需在后端配置匹配:“跨域请求令牌丢失” 故障
排查 1:后端是否开启allowCredentials: true—— 跨域场景下需允许携带 Cookie,否则前端无法获取XSRF-TOKEN Cookie;排查 2:前端是否正确配置withCredentials—— 使用 axios 时需设置axios.defaults.withCredentials = true,确保跨域请求携带 Cookie;排查 3:网关是否转发令牌 —— 若通过网关路由,需确保网关不拦截X-XSRF-TOKEN请求头,且正确转发用户会话信息(如JSESSIONID Cookie)。CSRF 防御并非单一配置,而是 “原理理解 - 场景适配 - 代码实现 - 效果验证 - 持续优化” 的闭环过程。在 Spring Boot3 项目中,需把握以下核心要点:
默认配置优先:充分利用 Spring Security 的默认 CSRF 防护(如 Cookie 存储令牌、表单自动注入令牌),避免重复造轮子;场景差异化适配:根据单体 / 前后端分离 / 微服务场景,调整令牌传递方式与跨域配置,确保防护不失效、不影响业务;多机制协同防护:结合角色权限、输入过滤、多因素认证,构建 “多层防护网”,降低单一防护失效的风险;定期验证与迭代:通过自动化测试(如 MockMvc)、漏洞扫描工具,定期验证防护效果,跟进 Spring Security 新版本的安全更新(如 Spring Security 6.x 对 CSRF 机制的优化)。通过以上实践,可有效抵御 CSRF 攻击,为 Spring Boot3 应用的安全稳定运行提供保障。建议开发者在项目初期就将 CSRF 防护纳入架构设计,而非后期补加 —— 毕竟,安全防护的成本,远低于漏洞修复的代价。
来源:从程序员到架构师一点号