Flurl中如何在同一会话中先后发起Get、POST请求解决CSRF校验失败问题
问题根因
你的代码存在两个核心问题直接导致CSRF校验失败:
- 未保持同一会话:CSRF令牌是和当前用户会话绑定的,Postman默认会自动持久化请求返回的Cookie、后续请求自动携带,因此可以正常调用。你当前两次请求是独立会话,POST请求使用的是新会话,和获取令牌的GET请求会话不匹配,即使令牌值正确也会校验失败。
- 请求头名称格式错误:标准的CSRF请求头名称为
X-CSRF-Token(中划线分隔),你当前代码用的是下划线X_CSRF_Token,部分服务端对请求头名称的分隔符、大小写敏感,无法识别下划线格式的请求头。
修正方案
你使用的是Flurl HTTP库,只需要保证两次请求共享同一个IFlurlClient实例、开启Cookie持久化,同时修正请求头名称即可:
1. 初始化共享会话客户端
// 在全局/类级别声明,保证两次请求用同一个客户端,维持会话 private readonly IFlurlClient sapClient = new FlurlClient(SA_BaseUrl) .WithBasicAuth("UserName", "Password") .WithCookies(); // 开启自动Cookie持久化,保持同一会话
2. 修正GET请求获取令牌代码
var result = await sapClient.Request() .AppendPathSegments("ProdOrderConfSet") .WithHeader("Accept","application/json") // 修正请求头名称为中划线格式 .WithHeader("X-CSRF-Token", "fetch") .GetAsync(); IReadOnlyNameValueList<string> headers = result.Headers; // 增加忽略大小写匹配,避免服务端返回的Header大小写不一致导致取值失败 string X_CSRF_Token = headers.FirstOrDefault(h => h.Name.Equals("x-csrf-token", StringComparison.OrdinalIgnoreCase)).Value;
3. 修正POST请求代码
var result2 = await sapClient.Request() .AppendPathSegments("ProdOrderConfSet") .WithHeader("Accept","application/json") .WithHeader("Content-Type", "application/json") // 修正请求头名称为中划线格式 .WithHeader("X-CSRF-Token", X_CSRF_Token) .PostJsonAsync(workStep) .ReceiveJson<DANTESAP_ProdWorkStep>();
可选校验点
如果修改后仍有问题,可以抓包对比Postman和C#代码的请求差异,重点核对:
- POST请求携带的Cookie是否和GET请求返回的会话Cookie完全一致
- 请求头
X-CSRF-Token的取值、格式是否和GET响应返回的完全一致
内容的提问来源于stack exchange,提问作者H. Saffour
相关产品推荐
相关产品推荐

