如何将Novell.Directory.Ldap与ASP.NET Core身份认证结合实现AD组验证?
Combining Novell.Directory.Ldap with ASP.NET Authentication
Absolutely! I’ve helped several developers pull off exactly this setup—combining Novell.Directory.Ldap for Active Directory checks with ASP.NET authentication. Let me walk you through the most common, reliable approaches folks use.
1. Custom Authentication Handler (ASP.NET Core)
If you’re working with ASP.NET Core, building a custom AuthenticationHandler is the most flexible way to integrate your existing LDAP logic. This lets you plug your AD validation directly into the framework’s authentication pipeline:
Step-by-Step Implementation
- Grab user credentials from the request (form data, headers, etc.)
- Use your existing Novell.Directory.Ldap code to validate the user’s AD credentials and check group membership
- If validation passes, generate a
ClaimsPrincipalwith user details and group-based claims - Attach the principal to the request to complete authentication
Example Code
public class LdapAuthHandler : AuthenticationHandler<AuthenticationSchemeOptions> { public LdapAuthHandler(IOptionsMonitor<AuthenticationSchemeOptions> options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock) : base(options, logger, encoder, clock) { } protected override async Task<AuthenticateResult> HandleAuthenticateAsync() { // Pull credentials from form (adjust for your auth method: Bearer, etc.) var username = Request.Form["username"].FirstOrDefault(); var password = Request.Form["password"].FirstOrDefault(); if (string.IsNullOrWhiteSpace(username) || string.IsNullOrWhiteSpace(password)) return AuthenticateResult.Fail("Missing username or password"); try { // Your existing LDAP validation logic using var ldapConn = new LdapConnection(); ldapConn.Connect("your-ad-server.domain.com", 389); ldapConn.Bind($"CN={username},OU=Employees,DC=domain,DC=com", password); // Check if user is in your target group var isAuthorized = IsUserInTargetGroup(ldapConn, username, "YourRestrictedGroup"); if (!isAuthorized) return AuthenticateResult.Fail("User not in required security group"); // Build claims for the authenticated user var claims = new List<Claim> { new Claim(ClaimTypes.Name, username), new Claim(ClaimTypes.Role, "RestrictedGroupMember") }; var identity = new ClaimsIdentity(claims, Scheme.Name); var principal = new ClaimsPrincipal(identity); var authTicket = new AuthenticationTicket(principal, Scheme.Name); return AuthenticateResult.Success(authTicket); } catch (LdapException ex) { return AuthenticateResult.Fail($"LDAP error: {ex.Message}"); } } // Your existing group-check logic (adapt to your AD structure) private bool IsUserInTargetGroup(LdapConnection conn, string username, string groupName) { var searchFilter = $"(&(objectClass=group)(name={groupName}))"; var searchResults = conn.Search("DC=domain,DC=com", LdapConnection.SCOPE_SUB, searchFilter, new[] { "member" }, false); while (searchResults.HasMore()) { var groupEntry = searchResults.Next(); var members = groupEntry.GetAttribute("member")?.StringValueArray; if (members != null && members.Any(m => m.Contains($"CN={username}"))) return true; } return false; } }
Register the Handler in Program.cs
builder.Services.AddAuthentication("LdapScheme") .AddScheme<AuthenticationSchemeOptions, LdapAuthHandler>("LdapScheme", opts => { }); builder.Services.AddAuthorization(opts => { opts.AddPolicy("RequireRestrictedGroup", policy => policy.RequireRole("RestrictedGroupMember")); });
2. Extend ASP.NET Identity (If You’re Using the Identity Framework)
If you already have ASP.NET Identity set up, you can extend its validation pipeline to include your LDAP checks:
Option: Custom User Validator
Create a custom IUserValidator that runs your LDAP validation alongside Identity’s default checks:
public class LdapUserValidator<TUser> : IUserValidator<TUser> where TUser : class { public async Task<IdentityResult> ValidateAsync(UserManager<TUser> manager, TUser user) { var username = await manager.GetUserNameAsync(user); // Note: Get the password from your login request context var password = ""; // Run LDAP auth check var isLdapAuthenticated = await ValidateLdapCredentials(username, password); var isInRequiredGroup = await CheckUserGroupMembership(username); if (!isLdapAuthenticated) return IdentityResult.Failed(new IdentityError { Description = "Invalid AD credentials" }); if (!isInRequiredGroup) return IdentityResult.Failed(new IdentityError { Description = "User not in authorized group" }); return IdentityResult.Success; } private async Task<bool> ValidateLdapCredentials(string username, string password) { try { using var conn = new LdapConnection(); conn.Connect("your-ad-server.domain.com", 389); conn.Bind($"CN={username},OU=Employees,DC=domain,DC=com", password); return true; } catch { return false; } } private async Task<bool> CheckUserGroupMembership(string username) { // Reuse your existing group-check logic here return true; } }
Register the Validator
builder.Services.AddScoped<IUserValidator<ApplicationUser>, LdapUserValidator<ApplicationUser>>();
Key Considerations
- Connection Pooling: Avoid creating a new LDAP connection for every request—implement a connection pool to boost performance.
- Secure Connections: Use LDAPS (port 636) instead of plain LDAP to encrypt credentials in transit.
- Least Privilege: Use a read-only AD service account for group queries instead of relying on the user’s credentials.
- Error Handling: Catch specific LDAP exceptions (e.g., invalid credentials, connection failures) to return meaningful feedback to users.
内容的提问来源于stack exchange,提问作者Paulo Cruz




