ASP.NET Core Web API通过HttpClient调用同服务器API遇Windows认证401错误
问题描述
两台运行在同一服务器上的ASP.NET Core 7.0 Web API服务,均启用Windows认证并使用不同的AD服务账户运行。本地开发环境中,通过HttpClient从第一个服务调用第二个服务时,Windows身份可正常传递并通过授权;但部署到服务器后,该调用出现HTTP 401未授权错误。
相关代码与日志信息:
受影响的控制器代码
[Authorize] [ApiController] [Route("[controller]")] public class WeatherController : Controller {
服务配置(Program.cs)
builder.Services.AddControllers(); builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(); builder.Services.AddAuthentication(NegotiateDefaults.AuthenticationScheme) .AddNegotiate(); builder.Services.AddAuthorization(options => { // 默认所有请求遵循默认授权策略 options.FallbackPolicy = options.DefaultPolicy; }); builder.Services.AddHttpContextAccessor(); var app = builder.Build();
HttpClient调用代码
using (HttpClient client = new HttpClient(new HttpClientHandler() { UseDefaultCredentials = true })) { HttpResponseMessage response = client.GetAsync(apiUrl + "Weather/GetWeather").Result; if (response.IsSuccessStatusCode) { weatherResults = JsonConvert.DeserializeObject<List<Weather>>(response.Content.ReadAsStringAsync().Result); } else { throw new InvalidOperationException($"failed: {response.StatusCode}"); } }
IIS日志对比
2023-08-04 21:53:44 xxx.xx.x.xxx GET /Weather/GetWeather/5afd17a9-5845-4e23-82e1-153a1927d047/2022-01-01T00:00:00/2023-08-04T17:53:24 - 443 - xxx.xx.x.xxx - - 401 2 5 0
2023-08-04 21:53:45 xxx.xx.x.xxx GET /Weather/GetWeather/5afd17a9-5845-4e23-82e1-153a1927d047/2022-01-01T00:00:00/2023-08-04T17:53:24 - 443 Domain\User xxx.xx.x.xxx - - 200 0 0 1427
本地开发时VS以个人账户运行,部署后服务以AD账户运行,需排查身份传递失败的原因。
解决建议
1. 配置Kerberos约束委派
当服务以AD账户运行并需要传递身份到另一服务时,Kerberos约束委派是核心解决方向:
- 在AD用户和计算机管理中,找到运行第一个API的AD服务账户
- 打开属性→委派标签,选择「信任此用户用于委派到指定服务」
- 添加第二个API服务对应的SPN(服务主体名称),格式通常为
HTTP/<目标API的主机名>和HTTP/<目标API的FQDN> - 用
setspn -L <账户名>命令验证第二个API的AD服务账户是否已注册对应SPN,未注册则用setspn -A HTTP/your-server-name DOMAIN\ServiceAccount2命令添加
2. 检查IIS认证配置
- 确保两台API所在的IIS站点均启用Windows认证,禁用匿名认证
- 进入IIS站点的「认证」→「Windows认证」→「高级设置」,勾选「启用内核模式认证」
- 确认站点应用程序池的运行身份为对应的AD服务账户,且该账户拥有站点目录的读写权限
3. 优化HttpClient的使用方式
- 避免直接创建
HttpClient实例,改用依赖注入注册:
在调用类中注入// Program.cs中注册HttpClient builder.Services.AddHttpClient("SecondApiClient", client => { client.BaseAddress = new Uri(apiUrl); }).ConfigurePrimaryHttpMessageHandler(() => new HttpClientHandler { UseDefaultCredentials = true, PreAuthenticate = true // 提前发送认证信息,减少协商次数 });IHttpClientFactory并使用异步调用:private readonly IHttpClientFactory _httpClientFactory; public YourService(IHttpClientFactory httpClientFactory) { _httpClientFactory = httpClientFactory; } public async Task<List<Weather>> GetWeather() { var client = _httpClientFactory.CreateClient("SecondApiClient"); var response = await client.GetAsync("Weather/GetWeather"); response.EnsureSuccessStatusCode(); return await JsonConvert.DeserializeObjectAsync<List<Weather>>(await response.Content.ReadAsStringAsync()); } - 替换同步调用(
.Result)为异步调用,避免线程上下文丢失导致身份传递失败
4. 验证NTLM策略设置
若Kerberos配置暂时无法生效,可临时检查NTLM限制:
- 打开「本地组策略编辑器」→「计算机配置」→「Windows设置」→「安全设置」→「本地策略」→「安全选项」
- 确认「网络安全:LAN管理器身份验证级别」设置为「发送NTLMv2响应\拒绝LM & NTLM」或更宽松级别(仅用于测试,优先使用Kerberos)
内容的提问来源于stack exchange,提问作者Vinod
相关产品推荐
相关产品推荐

