在 ASP.NET Core 中使用两个 AD 进行自定义身份验证

Custom authentication with two ADs in ASP.NET Core

我有一个 ASP.NET Core 5 应用程序,它在 Windows 服务器上使用身份框架。当用户在登录页面点击提交时,需要

  1. 根据 AD #1 验证他们的用户名和密码。
  2. 如果失败,则需要根据 AD #2 对它们进行身份验证。
  3. 如果同样失败,它应该给出一个错误。

这个 post 涵盖了 AD 身份验证,但我不知道它应该去哪里或如何触发:

此答案提供了有关创建自定义身份验证的详细信息,但如何将其集成到已有 UserManager 的身份框架中?

如果我只想通过身份验证锁定应用程序,我是否需要身份库?有更简单的方法吗?

谢谢。

更新: 请参阅下面的 RazorPages 答案。类似的逻辑可以用于 MVC 版本。

要使其与 RazorPages 一起使用,我们需要在 Startup.cs 中添加以下内容:

public void ConfigureServices(IServiceCollection services)
{
    // Other statements...

    services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
        .AddCookie(x => x.LoginPath = "/login");
}

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{ 
    // Other statements...
    
    app.UseAuthentication();
    app.UseAuthorization();
}

然后在 PageModel 我们要验证的页面上,我们将 [Authorize] 属性添加到 class.

然后在 LoginModel PageModel 中:

public class LoginModel : PageModel
{
    [BindProperty]
    [DisplayName("Username:")]
    [Required]
    public string Username { get; set; }

    [BindProperty]
    [Required]
    [DataType(DataType.Password)]
    [DisplayName("Password:")]
    public string Password { get; set; }

    public string ReturnUrl { get; set; }

    public void OnGet(string returnUrl = null)
    {
        ReturnUrl = returnUrl;
    }

    public async Task<IActionResult> OnPostAsync(string returnUrl = null)
    {
        if (!ModelState.IsValid)
        {
            return Page();
        }

        // Check both LDAPs. User must authenticate against one
        var isAuthenticated = IsAuthenticated(ConfigurationManager.AppSettings["MyFirstLDAPPath"]) || IsAuthenticated(ConfigurationManager.AppSettings["MySecondLDAPPath"]);

        if (isAuthenticated)
        {
            // Must provide at least the Username claim or it will throw an InvalidOperationException
            var identity = new ClaimsIdentity(new List<Claim>{ new Claim(ClaimTypes.Name, Username, ClaimValueTypes.String, "SomeUniqueValue") }, CookieAuthenticationDefaults.AuthenticationScheme);

            var principal = new ClaimsPrincipal(identity);
            await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, principal,
                new AuthenticationProperties());
            return LocalRedirect(returnUrl);
        }

        ViewData["Message"] = "Invalid credentials.";
        ReturnUrl = returnUrl;
        return Page();
    }

    // This is a reliable way to check the user's credentials.
    // Note that this also considers users with a changed password,
    // as some other techniques don't
    private bool IsAuthenticated(string ldapPath)
    {
        try
        {
            var conn = new LdapConnection(ldapPath);
            conn.Credential = new NetworkCredential(Username, Password);
            conn.Bind();
            return true;
        }
        catch (Exception)
        {
            return false;
        }
    }
}

然后在Login.cshtml:

<form asp-route-returnurl="@returnUrl" method="post">
    @Html.AntiForgeryToken()
    <div asp-validation-summary="ModelOnly" class="text-danger"></div>
    @if (!string.IsNullOrEmpty(ViewData["Message"]?.ToString()))
    {
        <span class="text-danger">
            @ViewData["Message"]
        </span>
    }
    @Html.HiddenFor(x => x.ReturnUrl)
    <h3 class="text-center text-info">Login</h3>
    <div class="form-group">
        <label asp-for="Username" class="text-info"></label>
        <input asp-for="Username" class="form-control" autofocus/>
        <span asp-validation-for="Username" class="text-danger"></span>
    </div>
    <div class="form-group">
        <label asp-for="Password" class="text-info"></label>
        <input asp-for="Password" class="form-control"/>
        <span asp-validation-for="Password" class="text-danger"></span>
    </div>
    <div class="form-group">
        <input type="submit" name="submit" class="btn btn-info btn-md" value="submit">
    </div>
</form>

希望这对其他人有帮助。