非MVC项目中React集成Antiforgery致POST请求400错误排查
ASP.NET Core + React 跨域防伪造Token验证400错误修复方案
问题根源
- 服务端在需要验证的POST/PUT/DELETE请求中重复调用
GetAndStoreTokens,每次都会生成新Token,导致请求携带的旧Token与服务端当前存储的Token不匹配,验证失败。 - Cookie的
SameSite=Strict配置在跨域场景下(不同端口属于跨域)会阻止浏览器发送Cookie,服务端无法拿到存储的Token进行验证。 - 客户端错误地在请求头中设置了
Access-Control-Allow-Credentials,该字段仅需服务端在响应头中返回,客户端设置无效且可能引发冲突。
修复步骤
服务端调整
- 限制Token生成时机:仅在GET请求(如首页、专门的Token接口)中生成并返回Anti-Forgery Token,避免在需要验证的请求中生成新Token覆盖旧值。
- 修正Cookie SameSite配置:将
SameSiteMode.Strict改为SameSiteMode.Lax(本地HTTP环境)或SameSiteMode.None(HTTPS环境,需配合Secure=true),确保跨域场景下Cookie能正常发送。 - 简化Token存储逻辑:
GetAndStoreTokens会自动处理Cookie的设置,无需手动调用context.Response.Cookies.Append,避免重复写入Cookie导致多个Token值。 - 完善CORS配置:确保CORS策略允许携带凭据(
.AllowCredentials()),并正确暴露必要的响应头。
客户端调整
- 移除无效请求头:删除请求头中的
Access-Control-Allow-Credentials字段。 - 确保Token获取时机:先发送GET请求获取Token,待Cookie写入后再携带Token发送POST/PUT/DELETE请求。
修正后代码示例
服务端Startup.cs代码
// 配置Anti-Forgery服务 services.AddAntiforgery(options => { options.HeaderName = "X-XSRF-TOKEN"; options.Cookie.Name = "XSRF-TOKEN"; options.Cookie.SameSite = SameSiteMode.Lax; // 跨域场景改为Lax或None options.Cookie.HttpOnly = false; options.Cookie.Secure = false; // 本地HTTP环境设为false,HTTPS环境改为true }); // 配置CORS services.AddCors(options => { options.AddPolicy("ReactCors", policy => { policy.WithOrigins("http://localhost:1000") .AllowAnyHeader() .AllowAnyMethod() .AllowCredentials() // 必须允许凭据 .WithExposedHeaders("X-XSRF-TOKEN"); }); }); // 中间件调整:仅在GET请求生成Token app.Use(async (context, next) => { var antiforgery = context.RequestServices.GetRequiredService<IAntiforgery>(); // 仅在GET请求时生成并存储Token if (HttpMethods.IsGet(context.Request.Method)) { antiforgery.GetAndStoreTokens(context); } await next(context); }); // 启用CORS(注意顺序:在UseRouting之后,UseAuthorization之前) app.UseCors("ReactCors");
客户端React请求代码
// 获取Cookie的工具函数 function getCookie(name) { const value = `; ${document.cookie}`; const parts = value.split(`; ${name}=`); if (parts.length === 2) return parts.pop().split(';').shift(); } // 先获取Token再发送POST请求 async function sendPostRequest() { // 先发起GET请求获取Token(比如访问服务根路径) await fetch('http://localhost:2000/', { credentials: 'include' }); // 准备POST请求头 const headers = new Headers(); headers.append('Content-Type', 'application/json'); headers.append('X-XSRF-TOKEN', getCookie('XSRF-TOKEN')); // 发送POST请求 const response = await fetch('http://localhost:2000/api/your-endpoint', { method: 'POST', credentials: 'include', headers: headers, body: JSON.stringify({ /* 请求体数据 */ }) }); if (response.ok) { // 处理成功响应 } else { // 处理错误 } }
补充说明
- 本地开发环境无需HTTPS也能正常工作,只要Cookie的
Secure设为false即可;生产环境建议启用HTTPS,并将Secure设为true,SameSite设为None。 - 确保
[ValidateAntiforgeryToken]特性仅应用于需要验证的POST/PUT/DELETE接口,GET接口无需添加。
内容的提问来源于stack exchange,提问作者Erik
相关产品推荐
相关产品推荐

