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

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的登录对话框,必须手动输入凭证才能完成认证。

需要解决的问题

  1. 如何实现非localhost访问时的Windows域自动单点登录?
  2. 如何消除Firefox的登录弹窗?(优先级较低,当前主要需解决Edge的问题)

解决方案

问题1:实现非localhost访问的Windows域自动SSO

Windows身份验证(NTLM/Kerberos)的自动SSO依赖客户端将目标站点加入本地Intranet区域,同时服务器需正确配置SPN以支持Kerberos(Kerberos比NTLM更易实现无交互自动认证)。

步骤1:将站点加入客户端本地Intranet区域

  • Edge/Chrome手动配置:
    1. 打开浏览器设置,搜索「本地Intranet」,进入Internet选项 → 安全 → 本地Intranet → 站点;
    2. 点击高级,添加站点地址http://my.machine.name:12345,勾选「包含所有不使用代理服务器的站点」,确定保存。
  • 域环境批量部署:
    使用组策略用户配置>管理模板>Windows组件>Internet Explorer>Internet控制面板>安全页>站点到区域分配列表,添加站点URL并分配到本地Intranet区域(编号1)。

步骤2:配置Kerberos SPN(域环境必填)

为运行IIS的服务账号注册SPN,确保Kerberos认证正常:

  1. 以域管理员身份打开命令提示符执行:
    setspn -A HTTP/my.machine.name:12345 DOMAIN\IIS_Service_Account
    setspn -A HTTP/my.machine.name DOMAIN\IIS_Service_Account
    
    替换DOMAIN\IIS_Service_Account为实际运行IIS应用池的域账号。
  2. 验证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信任站点:

  1. 在地址栏输入about:config,确认风险提示;
  2. 搜索network.automatic-ntlm-auth.trusted-uris,双击修改值为http://localhost:12345,http://my.machine.name:12345;
  3. 搜索network.negotiate-auth.trusted-uris,设置相同的值;
  4. 重启Firefox后生效。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.09 22:14:54