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

IdentityServer4在IIS上Windows认证失败报401但Visual Studio运行正常

问题描述

我正在尝试使用IdentityServer4(4.0.0版本)实现Windows认证功能,该程序在Visual Studio中运行完全正常,但部署到IIS后,输入凭证后会持续弹出Windows认证窗口,返回401状态码。我还尝试部署了Duende Software的官方示例源码,但也出现了相同的问题,我认为是我这边存在部分配置遗漏,恳请各位帮忙解决。
登录页

相关代码

Program.cs

public static IHostBuilder CreateHostBuilder(string[] args) =>
        Host.CreateDefaultBuilder(args)
            .UseSerilog()
            .ConfigureWebHostDefaults(webBuilder =>
            {
                webBuilder.UseStartup<Startup>();
            });

launchSettings.json

"windowsAuthentication": true,

启动配置

ExternalController.cs

public async Task<IActionResult> Challenge(string scheme, string returnUrl)
{
    if (string.IsNullOrEmpty(returnUrl)) returnUrl = "~/";
    
    if(scheme ==  "Windows")
    {
        return await ChallengeWindowsAsync(returnUrl);
    }
    
    // 验证returnUrl:要么是合法的OIDC URL,要么是本地页面地址
    if (Url.IsLocalUrl(returnUrl) == false && _interaction.IsValidReturnUrl(returnUrl) == false)
    {
        // 用户可能点击了恶意链接,需记录日志
        throw new Exception("invalid return URL");
    }
    
    // 启动认证挑战,将return URL和scheme带回
    var props = new AuthenticationProperties
    {
        RedirectUri = Url.Action(nameof(Callback)), 
        Items =
        {
            { "returnUrl", returnUrl }, 
            { "scheme", scheme },
        }
    };

    return Challenge(props, scheme);
    
}
//ChallengeWindowsAsync方法
private async Task<IActionResult> ChallengeWindowsAsync(string returnUrl)
{

    // 检查Windows认证是否已请求并验证通过
    var result = await HttpContext.AuthenticateAsync("Windows");

    if (result?.Principal is WindowsPrincipal wp)
    {
        // 我们将颁发外部Cookie,然后将用户重定向回外部回调,本质上将Windows认证视为与其他外部认证机制相同
        var props = new AuthenticationProperties()
        {
            RedirectUri = Url.Action("Callback"),
            Items =
            {
                { "returnUrl", returnUrl },
                { "scheme", "Windows" },
            }
        };

        var id = new ClaimsIdentity("Windows");

        // sid是sub声明的优质取值
        id.AddClaim(new Claim(JwtClaimTypes.Subject, wp.FindFirst(ClaimTypes.PrimarySid).Value));

        // 账户名是最接近展示名的字段
        id.AddClaim(new Claim(JwtClaimTypes.Name, wp.Identity.Name));

        // 将组作为声明添加——如果组数量过多请谨慎操作
        var wi = wp.Identity as WindowsIdentity;

        // 将组SID转换为展示名
        var groups = wi.Groups.Translate(typeof(NTAccount));
        var roles = groups.Select(x => new Claim(JwtClaimTypes.Role, x.Value));
        id.AddClaims(roles);


        await HttpContext.SignInAsync(
            IdentityServerConstants.ExternalCookieAuthenticationScheme,
            new ClaimsPrincipal(id),
            props);
        return Redirect(props.RedirectUri);
    }
    else
    {
        // 触发Windows认证
        // 由于Windows认证不支持redirect uri,调用挑战时会重新触发此URL
        return Challenge("Windows");
    }
}

IIS配置情况

已开启Windows authentication
IIS配置
IIS配置

解决方案

按优先级依次排查以下配置项:

  • 首先确认IIS站点的匿名认证必须和Windows认证同时开启。IdentityServer的大部分端点需要匿名访问,只有Windows挑战的接口需要走Windows认证,如果关闭匿名会导致所有请求都触发Windows校验,出现反复弹框的问题。
  • 检查站点应用程序池的.NET CLR版本是否设置为无托管代码,托管管道模式设置为集成。
  • 确认部署后站点根目录的web.config已经自动生成了正确的认证配置,需包含以下节点:
<system.webServer>
  <security>
    <authentication>
      <windowsAuthentication enabled="true" />
      <anonymousAuthentication enabled="true" />
    </authentication>
  </security>
  <aspNetCore processPath=".\你的程序名.exe" stdoutLogEnabled="false" stdoutLogFile=".\logs\stdout" hostingModel="inprocess" />
</system.webServer>

注意必须使用inprocess托管模型,OutOfProcess模式下Windows认证无法正常传递身份信息到ASP.NET Core程序。

  • 如果你是用域名访问站点,需要在客户端的Internet选项->本地Intranet->站点->高级里,把你的站点域名加入到本地Intranet区域,避免Windows认证默认不传递当前登录用户凭证到非内网站点。
  • 检查IIS的Windows认证提供程序顺序,把NTLM调到Negotiate前面,部分域环境下Kerberos配置不全会导致Negotiate认证失败,降级用NTLM即可正常工作。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.25 00:24:03