You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ASP.NET Core API中AntiforgeryToken引发400错误的原因排查

问题

在ASP.NET Core项目中,View通过JavaScript调用SiteController的PhoneNumberAuth(int app_idx, string memberPhone)方法,该方法内部用HttpClient调用另一个ApiController的PhoneNumberAuth接口时,出现AntiforgeryToken验证失败的400 Bad Request错误。给ApiController接口加[IgnoreAntiforgeryToken]能返回200,但不能使用这个属性。已在View和SiteController中获取并传递AntiforgeryToken,需排查错误原因及代码遗漏点。

相关代码

SiteController调用代码

[HttpPost]
public async Task<IActionResult> PhoneNumberAuth(int app_idx, string memberPhone)
{
    // 省略其他业务代码
    HttpClient client = new HttpClient();

    string url = "https://" + HttpContext.Request.Host.ToString() + Url.Action("PhoneNumberAuth", "Api")!;

    AntiforgeryTokenSet requestTokenSet = _antiforgery.GetAndStoreTokens(HttpContext);
    string requestTokenHeaderName = requestTokenSet.HeaderName!;
    string requestToken = requestTokenSet.RequestToken!;

    client.DefaultRequestHeaders.Accept.Clear();
    client.DefaultRequestHeaders.Add(requestTokenHeaderName, requestToken);

    var authenticationNumber = new
    {
        receiveNumber = memberPhone,
        senderNumber = smsInfo.sms_phoneNumber
    };

    string dataJson = Newtonsoft.Json.JsonConvert.SerializeObject(authenticationNumber);
    StringContent content = new StringContent(dataJson, Encoding.UTF8, "application/json");

    HttpResponseMessage response = await client.PostAsync(url, content);

    return Ok();
}

ApiController接口代码

[HttpPost("[Action]")]
public async Task<IActionResult> PhoneNumberAuth()
{
    // 省略其他业务代码
    return Ok();
}

View调用代码

@{ 
    var requestToken = Antiforgery.GetAndStoreTokens(Context);
}
...
$.ajax({
    url: '@Url.Action("PhoneNumberAuth", "Api")',
    headers:
    {
        '@requestToken.HeaderName': '@requestToken.RequestToken'
    },
    type: 'post',
    success: function(result) {
                            
    },
    error: function(ajaxContext) {
                            
    }
});

SiteController注入代码

private readonly IAntiforgery _antiforgery;

public SiteController(IAntiforgery antiforgery)
{
    _antiforgery = antiforgery;
}
// 省略其他代码
AntiforgeryTokenSet requestTokenSet = _antiforgery.GetAndStoreTokens(HttpContext);
错误原因分析
  • Token上下文不匹配:SiteController中调用GetAndStoreTokens生成的Token,基于View发起的当前请求上下文,但HttpClient调用ApiController是全新的服务器端请求,未携带对应Cookie中的验证Token。Antiforgery验证要求请求头的RequestToken与Cookie中的CookieToken配对使用,仅传RequestToken无法通过验证。
  • HttpClient未携带Cookie:服务器端用HttpClient发起请求时,默认不会自动携带当前HttpContext中的Cookie,而Antiforgery验证依赖Cookie内的Token与请求头Token做匹配。
修复方案

方案1:传递当前请求的Cookie到HttpClient

复制当前请求的Cookie到HttpClient的Cookie容器,同时复用当前请求已有的Antiforgery Token,确保配对:

[HttpPost]
public async Task<IActionResult> PhoneNumberAuth(int app_idx, string memberPhone)
{
    // 省略其他业务代码
    var clientHandler = new HttpClientHandler();
    clientHandler.CookieContainer = new CookieContainer();

    // 复制当前请求的所有Cookie到HttpClient容器
    foreach (var cookie in HttpContext.Request.Cookies)
    {
        clientHandler.CookieContainer.Add(new Cookie(cookie.Key, cookie.Value, "/", HttpContext.Request.Host.Host));
    }

    HttpClient client = new HttpClient(clientHandler);

    string url = "https://" + HttpContext.Request.Host.ToString() + Url.Action("PhoneNumberAuth", "Api")!;

    // 获取当前请求已有的Token,而非重新生成
    var tokens = _antiforgery.GetTokens(HttpContext);
    string requestTokenHeaderName = tokens.RequestTokenName!;
    string requestToken = tokens.RequestToken!;

    client.DefaultRequestHeaders.Accept.Clear();
    client.DefaultRequestHeaders.Add(requestTokenHeaderName, requestToken);

    // 后续序列化和请求代码不变
    var authenticationNumber = new
    {
        receiveNumber = memberPhone,
        senderNumber = smsInfo.sms_phoneNumber
    };

    string dataJson = Newtonsoft.Json.JsonConvert.SerializeObject(authenticationNumber);
    StringContent content = new StringContent(dataJson, Encoding.UTF8, "application/json");

    HttpResponseMessage response = await client.PostAsync(url, content);

    return Ok();
}

方案2:针对内部调用跳过验证(非全局忽略)

为ApiController接口添加内部调用标识,仅允许指定来源的请求跳过验证:

  1. 在ApiController接口中添加判断逻辑:
[HttpPost("[Action]")]
public async Task<IActionResult> PhoneNumberAuth()
{
    // 检查是否为内部调用
    if (Request.Headers.ContainsKey("X-Internal-Call") && Request.Headers["X-Internal-Call"] == "True")
    {
        // 执行内部业务逻辑
        return Ok();
    }
    // 外部调用走正常Antiforgery验证
    return Ok();
}
  1. 在SiteController的HttpClient请求中添加标识Header:
client.DefaultRequestHeaders.Add("X-Internal-Call", "True");

注意事项

  • 服务器端内部调用时,不要重新生成Antiforgery Token,需复用当前请求已有的Token,确保Cookie与请求头Token配对。
  • 分布式部署场景下,需确保所有实例的Antiforgery密钥一致,避免Token验证失败。

内容的提问来源于stack exchange,提问作者jium

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.17 23:10:12