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接口添加内部调用标识,仅允许指定来源的请求跳过验证:
- 在
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(); }
- 在
SiteController的HttpClient请求中添加标识Header:
client.DefaultRequestHeaders.Add("X-Internal-Call", "True");
注意事项
- 服务器端内部调用时,不要重新生成Antiforgery Token,需复用当前请求已有的Token,确保Cookie与请求头Token配对。
- 分布式部署场景下,需确保所有实例的Antiforgery密钥一致,避免Token验证失败。
内容的提问来源于stack exchange,提问作者jium
相关产品推荐
相关产品推荐

