Edge+IIS+ASP.NET Core 6.0环境下Windows域单点登录失效问题
ASP.NET Core Yarp反向代理Windows域自动认证问题
我在IIS中托管了一个基于ASP.NET Core 6.0(使用Microsoft Yarp.ReverseProxy)的反向代理应用,目标是通过X-Authenticated-Username请求头传递已认证用户的Windows域账号。
实现代码
Server.cs(重命名自Program.cs)
using System; using System.Diagnostics; using System.Net; using System.Net.Http; using System.Net.Security; using Microsoft.AspNetCore.Authentication.Negotiate; using Microsoft.AspNetCore.Builder; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using Yarp.ReverseProxy.Forwarder; namespace Foo; public class Server { // 实际代码中从配置读取 private const string ConnectionTimeout = "45"; private const string DestinationPrefix = "http://initech.com/stapler/swingline/red"; private const string HeaderName = "X-Authenticated-Username"; private const string RequestTimeout = "123"; public static void Main(string[] args) { var builder = WebApplication.CreateBuilder(args); { var services = builder.Services; services.AddHttpForwarder(); services.AddAuthentication(NegotiateDefaults.AuthenticationScheme).AddNegotiate(); // 注:IIS*Options配置项无实际作用,暂保留 // Out of Process services.Configure<IISOptions>(options => { options.AuthenticationDisplayName = "Foo Proxy (IIS Out-of-Process)"; options.AutomaticAuthentication = true; }); // In-Process services.Configure<IISServerOptions>(options => { options.AuthenticationDisplayName = "Foo Proxy (IIS In-Process)"; options.AutomaticAuthentication = true; }); } var app = builder.Build(); app.UseRouting(); app.UseAuthentication(); var httpClient = new HttpMessageInvoker(new SocketsHttpHandler { UseProxy = false, AllowAutoRedirect = false, AutomaticDecompression = DecompressionMethods.None, UseCookies = false, ActivityHeadersPropagator = new ReverseProxyPropagator(DistributedContextPropagator.Current), ConnectTimeout = TimeSpan.FromSeconds(Server.ConnectionTimeout), }); var transformer = new AddAuthenticatedUserHeader(Server.HeaderName); var requestConfig = new ForwarderRequestConfig { ActivityTimeout = TimeSpan.FromSeconds(Server.RequestTimeout), }; app.UseEndpoints(endpoints => { endpoints.MapForwarder("/{**catch-all}", Server.DestinationPrefix, requestConfig, transformer, httpClient); }); app.Run(); } }
AddAuthenticatedUserHeader.cs
using System.Net.Http; using System.Threading; using System.Threading.Tasks; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Yarp.ReverseProxy.Forwarder; namespace Foo; public sealed class AddAuthenticatedUserHeader : HttpTransformer { public string HeaderName { get; } public AddAuthenticatedUserHeader(string headerName) { this.HeaderName = headerName; } public override async ValueTask TransformRequestAsync(HttpContext httpContext, HttpRequestMessage proxyRequest, string destinationPrefix, CancellationToken cancellationToken) { await base.TransformRequestAsync(httpContext, proxyRequest, destinationPrefix, cancellationToken); var logger = httpContext.RequestServices.GetRequiredService<ILogger<AddAuthenticatedUserHeader>>(); // 如果获取到用户名,添加请求头 var userName = httpContext.User.Identity?.Name; if (userName is not null) { logger.LogInformation("通过'{header}'头传递已认证用户名:{name}", this.HeaderName, userName); proxyRequest.Headers.Add(this.HeaderName, userName); } else { logger.LogInformation("未获取到已认证用户名,不设置'{header}'头", this.HeaderName); } } }
问题现象
代理功能正常,但Windows域账号自动认证存在异常:
IIS站点仅启用Windows Authentication,监听端口12345,不同访问方式表现如下:
- 访问
http://localhost:12345:- Edge、Chrome、Opera可自动完成认证,无登录弹窗,请求头正常设置;
- Firefox已启用SSO,但会弹出类Basic Auth的登录对话框,无需输入凭证,多次点击登录后可完成认证,请求头正常设置。
- 访问
http://my.machine.name:12345:- Firefox表现同上;
- Edge弹出Windows安全对话框,显示域名,但需手动确认;
- Chrome、Opera弹出类Basic Auth的登录对话框,必须手动输入凭证才能完成认证。
需要解决的问题
- 如何实现非localhost访问时的Windows域自动单点登录?
- 如何消除Firefox的登录弹窗?(优先级较低,当前主要需解决Edge的问题)
解决方案
问题1:实现非localhost访问的Windows域自动SSO
Windows身份验证(NTLM/Kerberos)的自动SSO依赖客户端将目标站点加入本地Intranet区域,同时服务器需正确配置SPN以支持Kerberos(Kerberos比NTLM更易实现无交互自动认证)。
步骤1:将站点加入客户端本地Intranet区域
- Edge/Chrome手动配置:
- 打开浏览器设置,搜索「本地Intranet」,进入Internet选项 → 安全 → 本地Intranet → 站点;
- 点击高级,添加站点地址
http://my.machine.name:12345,勾选「包含所有不使用代理服务器的站点」,确定保存。
- 域环境批量部署:
使用组策略用户配置>管理模板>Windows组件>Internet Explorer>Internet控制面板>安全页>站点到区域分配列表,添加站点URL并分配到本地Intranet区域(编号1)。
步骤2:配置Kerberos SPN(域环境必填)
为运行IIS的服务账号注册SPN,确保Kerberos认证正常:
- 以域管理员身份打开命令提示符执行:
替换setspn -A HTTP/my.machine.name:12345 DOMAIN\IIS_Service_Account setspn -A HTTP/my.machine.name DOMAIN\IIS_Service_AccountDOMAIN\IIS_Service_Account为实际运行IIS应用池的域账号。 - 验证SPN注册结果:
setspn -L DOMAIN\IIS_Service_Account
步骤3:完善ASP.NET Core认证配置
在现有代码中添加授权中间件,强制未认证请求被拦截:
app.UseAuthentication(); app.UseAuthorization(); // 添加此行 // 配置转发路由时添加授权要求 app.UseEndpoints(endpoints => { endpoints.MapForwarder("/{**catch-all}", Server.DestinationPrefix, requestConfig, transformer, httpClient) .RequireAuthorization(); });
问题2:消除Firefox的登录弹窗
手动配置Firefox信任站点:
- 在地址栏输入
about:config,确认风险提示; - 搜索
network.automatic-ntlm-auth.trusted-uris,双击修改值为http://localhost:12345,http://my.machine.name:12345; - 搜索
network.negotiate-auth.trusted-uris,设置相同的值; - 重启Firefox后生效。
内容的提问来源于stack exchange,提问作者Zastai
相关产品推荐
相关产品推荐

