如何在ASP.NET Core 2.0中通过LDAP接口暴露ASP.NET Identity
Great question! Yes, you absolutely can expose your ASP.NET Identity users via an LDAP interface directly from your ASP.NET Core 2.0 app—here's how to approach it:
While .NET doesn't have a direct equivalent to Java's UnboundID LDAP SDK for server-side implementation, there are practical, maintainable ways to build a lightweight LDAP server layer on top of your existing Identity system. Below is a step-by-step guide using open-source tools and custom integration.
1. Choose a .NET LDAP Server Library
Avoid reinventing the wheel by leveraging existing open-source frameworks designed for .NET Core:
- LdapServer.NET: A lightweight, purpose-built LDAP server framework for .NET Core that lets you customize core operations like binding (authentication) and searching (user queries).
- SharpLDAPServer: Another open-source option with basic LDAP server abstractions, ideal for quick integration.
We'll use LdapServer.NET for this example—install it via NuGet first:
Install-Package LdapServer.NET
2. Integrate ASP.NET Identity with LDAP Logic
The core idea is mapping LDAP's core operations to ASP.NET Identity's UserManager service:
- Bind Request: Maps to user authentication (verify username/password)
- Search Request: Maps to querying users from your Identity store
Step 1: Configure LDAP Server in Startup
In Startup.cs (ASP.NET Core 2.0's configuration file), register the LDAP server and link it to your existing Identity setup:
public void ConfigureServices(IServiceCollection services) { // Keep your existing ASP.NET Identity configuration services.AddDefaultIdentity<IdentityUser>() .AddEntityFrameworkStores<ApplicationDbContext>(); // Configure and add LDAP server services services.AddLdapServer(options => { options.Port = 3890; // Use non-privileged port for development; switch to 389 in production (requires admin rights) options.BaseDn = "dc=yourapp,dc=com"; // Your LDAP root domain name }) .AddBindHandler<IdentityLdapBindHandler>() // Handles user authentication .AddSearchHandler<IdentityLdapSearchHandler>(); // Handles user search queries } public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { // Keep your existing middleware (MVC, static files, etc.)... // Start the LDAP server alongside your web app app.UseLdapServer(); }
Step 2: Implement LDAP Bind Handler (Authentication)
This handler processes LDAP login requests, parses the LDAP Distinguished Name (DN) to extract the username, and validates credentials using ASP.NET Identity:
public class IdentityLdapBindHandler : IBindHandler { private readonly UserManager<IdentityUser> _userManager; public IdentityLdapBindHandler(UserManager<IdentityUser> userManager) { _userManager = userManager; } public async Task<LdapResult> HandleBindAsync(BindRequest request, CancellationToken cancellationToken) { // Extract username from LDAP DN (e.g., "uid=johndoe,dc=yourapp,dc=com" → "johndoe") var uidSegment = request.Dn.Split(',').FirstOrDefault(seg => seg.StartsWith("uid=")); if (uidSegment == null) { return LdapResult.InvalidCredentials; } var username = uidSegment.Substring(4); // Validate user and password with ASP.NET Identity var user = await _userManager.FindByNameAsync(username); if (user == null) { return LdapResult.InvalidCredentials; } var isPasswordValid = await _userManager.CheckPasswordAsync(user, request.Password); return isPasswordValid ? LdapResult.Success : LdapResult.InvalidCredentials; } }
Step 3: Implement LDAP Search Handler (User Queries)
This handler processes LDAP search requests, fetches users from your Identity store, and converts them into LDAP-compatible entries:
public class IdentityLdapSearchHandler : ISearchHandler { private readonly UserManager<IdentityUser> _userManager; private readonly string _baseDn; public IdentityLdapSearchHandler(UserManager<IdentityUser> userManager, IOptions<LdapServerOptions> ldapOptions) { _userManager = userManager; _baseDn = ldapOptions.Value.BaseDn; } public async Task<IEnumerable<LdapEntry>> HandleSearchAsync(SearchRequest request, CancellationToken cancellationToken) { var ldapEntries = new List<LdapEntry>(); // Fetch users (simplified to all users; extend with filter logic based on LDAP search criteria) var users = await _userManager.Users.ToListAsync(cancellationToken); foreach (var user in users) { var entryDn = $"uid={user.UserName},{_baseDn}"; // Build LDAP attributes to match the third-party service's expected schema var attributes = new Dictionary<string, List<string>> { { "objectClass", new List<string> { "top", "person", "inetOrgPerson" } }, { "uid", new List<string> { user.UserName } }, { "cn", new List<string> { user.UserName } }, { "sn", new List<string> { user.UserName } }, { "mail", new List<string> { user.Email ?? string.Empty } } }; ldapEntries.Add(new LdapEntry(entryDn, attributes)); } return ldapEntries; } }
3. Key Considerations
- Port Permissions: Default LDAP ports (389 for unencrypted, 636 for LDAPS) require admin privileges to bind. Use a non-privileged port like 3890 during development.
- Security: For production, enable LDAPS (encrypted connections) by configuring an SSL certificate for your LDAP server—refer to your library's documentation for setup steps.
- Schema Compatibility: Adjust the LDAP attributes and
objectClassvalues in the search handler to match the third-party service's requirements. - Performance: Add indexes to frequently queried Identity user fields (like
UserName) and implement pagination/filtering in the search handler if you have a large user base.
Alternative: Custom LDAP Protocol Implementation
If you can't find a suitable library, you could implement LDAP from scratch by parsing ASN.1 BER-encoded requests (LDAP uses this protocol). However, this requires deep knowledge of LDAP specifications and is only recommended for specialized use cases.
内容的提问来源于stack exchange,提问作者Sebastian Brandes

