尝试 Windows 身份验证时出现“没有为方案 'Windows' 注册身份验证处理程序”错误

时间:2020-12-30 21:56:05

标签: identityserver4 windows-authentication

我想为由 IIS 托管的 IdentityServer 启用 Windows 身份验证(进行中)。 IdentityServer4 的文档指出“您通过在 Windows 方案上调用 ChallengeAsync 来触发 Windows 身份验证”,但它确实说明了在哪里以及如何进行。我假设它在 AccountController 的登录功能中,但它似乎对我不起作用。

这是我在运行 IdentityServer 时遇到的错误 enter image description here

但据我所知,我已经注册了 Windows 方案的身份验证。以下是身份服务器的 Startup.cs 的 ConfigurtionServices 和 Confiuration 函数:

public void ConfigureServices(IServiceCollection services)
{
    IdentityModelEventSource.ShowPII = true; 
    services.AddControllersWithViews();

    var connstr = Configuration.GetConnectionString("DBConnection");
    services.AddDbContext<ApplicationDbContext>(options => options.UseSqlServer(connstr));

    services.AddIdentity<ApplicationUser, IdentityRole>()
        .AddEntityFrameworkStores<ApplicationDbContext>()
        .AddDefaultTokenProviders();

    var builder = services.AddIdentityServer(options =>
    {
        options.Events.RaiseErrorEvents = true;
        options.Events.RaiseInformationEvents = true;
        options.Events.RaiseFailureEvents = true;
        options.Events.RaiseSuccessEvents = true;
        options.EmitStaticAudienceClaim = true;
    })
        .AddAspNetIdentity<ApplicationUser>()
        .AddConfigurationStore(options =>
        {
            options.ConfigureDbContext = b => b.UseSqlServer(connstr,
                sql => sql.MigrationsAssembly(migrationsAssembly));
        })
        .AddOperationalStore(options =>
        {
            options.ConfigureDbContext = b => b.UseSqlServer(connstr,
                sql => sql.MigrationsAssembly(migrationsAssembly));
        });

    // not recommended for production - you need to store your key material somewhere secure
    builder.AddDeveloperSigningCredential();
    //services.AddIdentityServer().AddSigningCredential(
    //    new X509Certificate2(Path.Combine(_environment.ContentRootPath, "certs", "IdentityServer4Auth.pfx")));

    // configures IIS in-proc settings
    services.Configure<IISServerOptions>(iis =>
    {
        iis.AuthenticationDisplayName = "Windows";
        iis.AutomaticAuthentication = false;
    });

    // for Windows authentication 
    services.AddAuthentication(IISDefaults.AuthenticationScheme);
}

public void Configure(IApplicationBuilder app)
{
    if (Environment.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
        app.UseDatabaseErrorPage();
    }

    // use MVC
    app.UseStaticFiles();
    app.UseRouting();

    app.UseIdentityServer();
    // use MVC
    app.UseAuthorization();
    app.UseEndpoints(endpoints =>
    {
        endpoints.MapDefaultControllerRoute();
    });
}

正如您在 ConfigurationServices 函数中看到的最后一行,我通过调用 services.AddAuthentication(IISDefaults.AuthenticationScheme) 为 Windows 注册了身份验证处理程序

这是 AccountController.cs 中的登录函数,我在其中调用 ChallengeWindowsAsync - 我认为这是 IdentityServer4 Doc 建议的方式,但我不确定。如果方法不对,应该如何改正?

[HttpGet]
public async Task<IActionResult> Login(string returnUrl)
{
    // trigger Windows authentication by calling ChallengeAsync
    await ChallengeWindowsAsync(returnUrl);

    // build a model so we know what to show on the login page
    var vm = await BuildLoginViewModelAsync(returnUrl);

    if (vm.IsExternalLoginOnly)
    {
        // we only have one option for logging in and it's an external provider
        return RedirectToAction("Challenge", "External", new { scheme = vm.ExternalLoginScheme, returnUrl });
    }

    return View(vm);
}

这里是 ChallengeWindowsAsync 函数

private async Task<IActionResult> ChallengeWindowsAsync(string returnUrl)
{
    // see if windows auth has already been requested and succeeded
    var result = await HttpContext.AuthenticateAsync("Windows");
    if (result?.Principal is WindowsPrincipal wp)
    {
        // we will issue the external cookie and then redirect the
        // user back to the external callback, in essence, treating windows
        // auth the same as any other external authentication mechanism
        var props = new AuthenticationProperties()
        {
            RedirectUri = Url.Action("Callback"),
            Items =
            {
                { "returnUrl", returnUrl },
                { "scheme", "Windows" },
            }
        };

        var id = new ClaimsIdentity("Windows");

        // the sid is a good sub value
        id.AddClaim(new Claim(JwtClaimTypes.Subject, wp.FindFirst(ClaimTypes.PrimarySid).Value));

        // the account name is the closest we have to a display name
        id.AddClaim(new Claim(JwtClaimTypes.Name, wp.Identity.Name));

        // add the groups as claims -- be careful if the number of groups is too large
        var wi = wp.Identity as WindowsIdentity;

        // translate group SIDs to display names
        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
    {
        // trigger windows auth
        // since windows auth don't support the redirect uri,
        // this URL is re-triggered when we call challenge
        return Challenge("Windows");
    }
}

0 个答案:

没有答案