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

.NET 6.0 ASP.NET Core MVC中LDAP认证:自定义登录逻辑与模板选择

ASP.NET Core 6 MVC 结合Windows 2016 LDAP认证问题解答

一、项目模板选择建议

  • 如果仅需LDAP表单认证,无需本地用户管理(注册、密码重置等),优先选Empty或MVC模板:无需引入Identity框架,自定义登录逻辑更灵活,项目更轻量。
  • 如果后续需要结合本地用户扩展(如给LDAP用户分配本地权限),或用到Identity的角色、Claims管理功能,可选择Individual Accounts模板:但需禁用默认的注册、密码重置等无关功能,替换登录逻辑为自定义LDAP认证。
  • 不建议选Windows Authentication模板:该模板是基于当前系统用户的自动登录,不符合手动输入账号密码的表单认证场景。

二、让登录Action使用自定义LDAP认证逻辑

默认的Identity登录Action无法直接修改,需自定义登录流程,步骤如下:

1. 配置Cookie认证中间件

在Program.cs中添加Cookie认证配置,并确保中间件顺序正确:

// 添加Cookie认证
builder.Services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
    .AddCookie(options =>
    {
        options.LoginPath = "/Account/Login"; // 未认证时跳转的登录页
        options.AccessDeniedPath = "/Account/AccessDenied"; // 权限不足跳转页
        options.ExpireTimeSpan = TimeSpan.FromHours(8); // 登录有效期
    });

// 中间件顺序:路由 → 认证 → 授权
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();

app.MapControllerRoute(
    name: "default",
    pattern: "{controller=Home}/{action=Index}/{id?}");

2. 创建自定义登录视图模型

public class LoginViewModel
{
    [Required]
    [Display(Name = "用户名")]
    public string UserName { get; set; }

    [Required]
    [DataType(DataType.Password)]
    [Display(Name = "密码")]
    public string Password { get; set; }

    [Display(Name = "记住我")]
    public bool RememberMe { get; set; }
}

3. 编写自定义Account控制器及登录Action

注入IAuthenticationService,调用LDAP认证逻辑,验证通过后生成Claims并完成登录:

using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies;
using System.Security.Claims;

public class AccountController : Controller
{
    private readonly IAuthenticationService _ldapAuthService;

    public AccountController(IAuthenticationService ldapAuthService)
    {
        _ldapAuthService = ldapAuthService;
    }

    [HttpGet]
    public IActionResult Login(string returnUrl = null)
    {
        ViewData["ReturnUrl"] = returnUrl;
        return View();
    }

    [HttpPost]
    [ValidateAntiForgeryToken]
    public async Task<IActionResult> Login(LoginViewModel model, string returnUrl = null)
    {
        ViewData["ReturnUrl"] = returnUrl;
        if (!ModelState.IsValid)
        {
            return View(model);
        }

        // 调用LDAP认证服务验证账号密码
        var authenticatedUser = _ldapAuthService.Login(model.UserName, model.Password);
        if (authenticatedUser != null)
        {
            // 构建用户Claims
            var claims = new List<Claim>
            {
                new Claim(ClaimTypes.Name, authenticatedUser.UserName),
                new Claim(ClaimTypes.DisplayName, authenticatedUser.DisplayName)
                // 可添加更多从LDAP获取的用户属性,如部门、邮箱等
            };

            var claimsIdentity = new ClaimsIdentity(
                claims, CookieAuthenticationDefaults.AuthenticationScheme);

            var authProperties = new AuthenticationProperties
            {
                IsPersistent = model.RememberMe,
                RedirectUri = returnUrl ?? Url.Action("Index", "Home")
            };

            // 完成登录
            await HttpContext.SignInAsync(
                CookieAuthenticationDefaults.AuthenticationScheme,
                new ClaimsPrincipal(claimsIdentity),
                authProperties);

            return LocalRedirect(returnUrl ?? Url.Action("Index", "Home"));
        }

        // 认证失败,添加错误信息
        ModelState.AddModelError(string.Empty, "用户名或密码不正确");
        return View(model);
    }

    [HttpPost]
    [ValidateAntiForgeryToken]
    public async Task<IActionResult> Logout()
    {
        await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
        return RedirectToAction("Index", "Home");
    }
}

4. 创建登录视图(Views/Account/Login.cshtml)

@model LoginViewModel

@{
    ViewData["Title"] = "登录";
}

<h1>@ViewData["Title"]</h1>
<div class="row">
    <div class="col-md-4">
        <form asp-action="Login" asp-route-returnUrl="@ViewData["ReturnUrl"]">
            <div asp-validation-summary="ModelOnly" class="text-danger"></div>
            <div class="form-group">
                <label asp-for="UserName" class="control-label"></label>
                <input asp-for="UserName" class="form-control" />
                <span asp-validation-for="UserName" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Password" class="control-label"></label>
                <input asp-for="Password" class="form-control" />
                <span asp-validation-for="Password" class="text-danger"></span>
            </div>
            <div class="form-group">
                <div class="checkbox">
                    <label asp-for="RememberMe">
                        <input asp-for="RememberMe" /> @Html.DisplayNameFor(model => model.RememberMe)
                    </label>
                </div>
            </div>
            <div class="form-group">
                <input type="submit" value="登录" class="btn btn-primary" />
            </div>
        </form>
    </div>
</div>

三、已完成的LDAP配置整理

以下是你已经实现的LDAP基础配置,可继续沿用:

1. 安装依赖包

安装NuGet包 Microsoft.Windows.Compatibility,用于支持LDAP相关的DirectoryEntry和DirectorySearcher类。

2. LDAP配置类与配置文件

  • 定义LdapConfig类:
public class LdapConfig
{
    public string Path { get; set; }
    public string UserDomainName { get; set; }
}
  • 在appsettings.json中添加LDAP节点:
{
  "Ldap": {
    "Path": "<<LDAP Path>>",
    "UserDomainName": "<<Domain Name>>"
  }
}

3. 配置依赖注入

在Program.cs中读取LDAP配置并注册认证服务:

// 读取LDAP配置
builder.Services.Configure<LdapConfig>(builder.Configuration.GetSection("Ldap"));
// 注册LDAP认证服务
builder.Services.AddScoped<IAuthenticationService, LdapAuthenticationService>();

4. 认证服务接口与实现

  • IAuthenticationService接口:
public interface IAuthenticationService
{
    User Login(string userName, string password);
}
  • User数据模型:
public class User
{
    public string UserName { get; set; }
    public string DisplayName { get; set; }
    // 其他属性可按需扩展
}
  • LdapAuthenticationService实现类:
public class LdapAuthenticationService : IAuthenticationService
{
    private const string DisplayNameAttribute = "DisplayName";
    private const string SAMAccountNameAttribute = "SAMAccountName";
    
    private readonly LdapConfig config;
        
    public LdapAuthenticationService(IOptions<LdapConfig> config)
    {
        this.config = config.Value;
    }
    public User Login(string userName, string password)
    {
        try
        {
            using (DirectoryEntry entry = new DirectoryEntry(config.Path, config.UserDomainName + "\\" + userName, password))
            {
                using (DirectorySearcher searcher = new DirectorySearcher(entry))
                {
                    searcher.Filter = string.Format("({0}={1})", SAMAccountNameAttribute, userName);
                    searcher.PropertiesToLoad.Add(DisplayNameAttribute);
                    searcher.PropertiesToLoad.Add(SAMAccountNameAttribute);
                    var result = searcher.FindOne();
                    if (result != null)
                    {
                        var displayName = result.Properties[DisplayNameAttribute];
                        var samAccountName = result.Properties[SAMAccountNameAttribute];
                        
                        return new User
                        {
                            DisplayName = displayName == null || displayName.Count <= 0 ? null : displayName[0].ToString(),
                            UserName = samAccountName == null || samAccountName.Count <= 0 ? null : samAccountName[0].ToString()
                        };
                    }
                }
            }
        }
        catch (Exception ex)
        {
            // 记录认证失败异常
        }
        return null;
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.17 16:40:53