启用CSRF后出现403认证错误的排查与解决求助
解决Spring Boot + Svelte启用CSRF后POST /auth/signin 403问题
问题背景
技术栈为Java Spring Boot、Svelte,启用CSRF防护后,调用POST /auth/signin接口时收到403 Full authentication is required to access this resource错误。具体操作流程:
- 登录前先发送GET
/csrf请求获取CSRF token,响应格式如下:
{ "token":"d4cb2245-be82-4b61-ace5-8d52c6068182", "parameterName":"_csrf", "headerName":"X-XSRF-TOKEN" }
- 将token放入
X-XSRF-TOKEN请求头后发送POST/auth/signin,仍触发错误。
后端已配置:
- 开启CSRF,使用
CookieCsrfTokenRepository,/csrf和/auth/**路径已放行;禁用CSRF可正常访问 - 提供
CsrfController处理GET/csrf请求
前端已实现获取CSRF token并加入请求头的逻辑,尝试添加有效Authorization头仍报错。开启Spring Security TRACE日志后,发现提示Invalid CSRF token found for http://127.0.0.1:8080/auth/signin,但用Postman携带X-XSRF-TOKEN头和Cookie: XSRF-TOKEN可正常访问,推测前端请求未携带Cookie。
核心原因
Spring Security的CookieCsrfTokenRepository验证逻辑要求:请求必须同时携带**X-XSRF-TOKEN请求头和XSRF-TOKEN Cookie**,两者值一致才会通过验证。Postman会自动保存并发送Cookie,但前端请求默认不会携带Cookie,导致验证失败。
解决方案
1. Svelte前端配置:确保请求携带Cookie
无论是同域还是跨域请求,都需要显式配置请求携带凭证:
- 如果用原生
fetch,添加credentials: 'include'参数 - 如果用
axios,添加withCredentials: true参数
示例代码(原生fetch):
// 获取CSRF token的请求,必须携带Cookie才能让后端设置XSRF-TOKEN Cookie async function getCsrf() { const response = await fetch('/csrf', { credentials: 'include' }); return response.json(); } // 登录请求,同时携带X-XSRF-TOKEN头和Cookie async function signin(userData) { const csrfData = await getCsrf(); const response = await fetch('/auth/signin', { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-XSRF-TOKEN': csrfData.token }, credentials: 'include', // 关键:携带Cookie body: JSON.stringify(userData) }); return response; }
2. 后端跨域配置(如果前端和后端跨域)
如果前端运行在不同域名/端口(比如Svelte默认的http://localhost:5173,后端在http://localhost:8080),需要在Spring Boot中配置CORS允许携带凭证:
@Configuration @EnableWebSecurity public class SecurityConfig { @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http .csrf(csrf -> csrf .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()) // 允许前端读取Cookie ) .authorizeHttpRequests(auth -> auth .requestMatchers("/csrf", "/auth/**").permitAll() .anyRequest().authenticated() ) .cors(cors -> cors.configurationSource(corsConfigurationSource())); // 启用CORS配置 return http.build(); } @Bean public CorsConfigurationSource corsConfigurationSource() { CorsConfiguration config = new CorsConfiguration(); config.setAllowedOrigins(List.of("http://localhost:5173")); // 替换为你的前端实际域名 config.setAllowedMethods(List.of("GET", "POST", "OPTIONS")); config.setAllowedHeaders(List.of("*")); config.setAllowCredentials(true); // 必须设置为true,允许携带Cookie UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); source.registerCorsConfiguration("/**", config); return source; } }
3. 排查验证步骤
- 打开浏览器开发者工具(F12),切换到Network标签:
- 查看GET
/csrf请求的Response Headers,确认存在Set-Cookie: XSRF-TOKEN=xxx - 查看POST
/auth/signin请求的Request Headers,确认:- 携带
Cookie: XSRF-TOKEN=xxx X-XSRF-TOKEN头的值和Cookie中的值一致
- 携带
- 查看GET
- 若跨域,检查Response Headers中的
Access-Control-Allow-Credentials是否为true,Access-Control-Allow-Origin是否是前端的具体域名(不能用*)
内容的提问来源于stack exchange,提问作者Chatchawan U-patham
相关产品推荐
相关产品推荐

