ASP.NET Core 6应用使用Negotiate认证时的握手错误
问题背景
我有一个ASP.NET Core 6应用,使用Negotiate认证方案通过Windows身份验证(NTLM/Kerberos)完成身份验证。本地开发机器上配置运行正常,但部署到生产服务器后出现问题:调用标记了[Authorize]的端点时,浏览器弹出登录提示,输入凭证后后端返回500错误。
关键现象:服务器内部通过https://localhost访问应用时认证正常;使用完整机器URL(如https://myserver.domain.local)访问时认证失败并返回500错误。
认证配置代码
services.AddAuthentication(NegotiateDefaults.AuthenticationScheme) .AddNegotiate(); services.AddAuthorization(options => { options.DefaultPolicy = new AuthorizationPolicyBuilder(NegotiateDefaults.AuthenticationScheme) .RequireAuthenticatedUser() .Build(); }
错误日志信息
Connection id "0HN6OHVHNEJKS", Request id "0HN6OHVHNEJKS:00000007": An unhandled exception was thrown by the application.
System.InvalidOperationException: An anonymous request was received in between authentication handshake requests.
at Microsoft.AspNetCore.Authentication.Negotiate.NegotiateHandler.HandleRequestAsync()
at Microsoft.AspNetCore.Authentication.Negotiate.NegotiateHandler.HandleRequestAsync()
at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context)
at Microsoft.AspNetCore.Session.SessionMiddleware.Invoke(HttpContext context)
at Microsoft.AspNetCore.Session.SessionMiddleware.Invoke(HttpContext context)
at Microsoft.AspNetCore.Diagnostics.ExceptionHandlerMiddleware.g__Awaited|6_0(ExceptionHandlerMiddleware middleware, HttpContext context, Task task)
at Microsoft.AspNetCore.Diagnostics.ExceptionHandlerMiddleware.HandleException(HttpContext context, ExceptionDispatchInfo edi)
at Microsoft.AspNetCore.Diagnostics.ExceptionHandlerMiddleware.g__Awaited|6_0(ExceptionHandlerMiddleware middleware, HttpContext context, Task task)
at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpProtocol.ProcessRequests[TContext](IHttpApplication`1 application)
排查与解决思路
1. 检查代理/负载均衡的请求转发配置
由于localhost访问正常、域名访问失败,大概率是代理或负载均衡中断了NTLM/Kerberos的握手流程:
- 确保代理(IIS、Nginx等)保留原始请求头,特别是
Authorization头,同时启用客户端IP传递 - 若使用IIS反向代理,关闭WebDAV模块或配置允许认证请求通过,避免拦截OPTIONS等预请求
- 负载均衡需启用粘性会话,NTLM/Kerberos握手需要同一个连接完成多轮请求,分散到不同实例会导致握手中断
2. 验证Kerberos/NTLM服务端配置
- 若使用Kerberos,检查服务器SPN(服务主体名称)注册是否正确:执行
setspn -L <服务器运行账号>,确认存在HTTP/myserver.domain.local和HTTP/myserver的SPN - 应用池运行账号需为域账号(本地账号仅支持NTLM),且拥有读取SPN的权限
- 检查本地安全策略:
网络安全: LAN管理器身份验证级别设置为兼容客户端的级别(如发送NTLMv2响应\拒绝LM & NTLM)
3. 抓包与日志调试
- 用Fiddler或Wireshark抓包,对比localhost和域名访问的请求差异:查看是否在握手过程中出现额外匿名请求(如浏览器自动发送的OPTIONS请求),以及认证头是否正确传递
- 开启详细认证日志,在
appsettings.json中添加:
{ "Logging": { "LogLevel": { "Microsoft.AspNetCore.Authentication": "Trace", "Microsoft.AspNetCore.Server.Kestrel": "Trace" } } }
通过日志追踪认证握手的每一步,定位匿名请求的来源。
4. 调整中间件顺序
从堆栈日志看SessionMiddleware在认证中间件之后执行,可能干扰认证状态传递。确保中间件顺序符合官方规范:
app.UseHttpsRedirection(); app.UseStaticFiles(); app.UseRouting(); app.UseAuthentication(); // 认证中间件优先于Session app.UseAuthorization(); app.UseSession(); // Session中间件放在认证之后
5. 临时禁用非必要中间件
暂时移除SessionMiddleware或其他非核心中间件,测试认证是否恢复正常。若恢复,再逐步排查该中间件的配置问题(如Session Cookie的SameSite设置、超时时间等)。
内容的提问来源于stack exchange,提问作者Master_T

