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

Blazor Server中LDAP与自定义AuthenticationStateProvider的集成方案咨询

How to integrate LDAP authentication logic with a custom AuthenticationStateProvider?

I'm trying to implement authentication using LDAP with a custom AuthenticationStateProvider in ASP.NET. I've already completed the following configurations:

  • Added services.AddScoped<AuthenticationStateProvider,Login_Logic>(); to the service collection
  • Configured app.UseAuthentication(); and app.UseAuthorization(); in Startup.cs

However, I'm unclear on how to integrate the LDAP authentication logic with my custom AuthenticationStateProvider. Here's my current Login_Logic class code:

public class Login_Logic:AuthenticationStateProvider {
    public UserData user { get; set; }
    public string loginattempt { get; set; }
    public bool userisauthenticated { get; set; }
    SearchResult result { get; set; }
    public override Task<AuthenticationState> GetAuthenticationStateAsync() {
        var identity = new ClaimsIdentity(new[] { new Claim(ClaimTypes.Name, result.GetDirectoryEntry().Name) }, "Fake authentication type");
    }
    private async Task dothething() {
        DirectoryEntry entry = new DirectoryEntry();
        entry = new DirectoryEntry("LDAP://myldap");
        entry.Username = user.UserName;
        entry.Password = user.Password;
        DirectorySearcher search = new DirectorySearcher(entry);
        search.Filter = "(SAMAccountName=" + user.UserName + ")";
        search.PropertiesToLoad.Add("phone");
        if (user.UserName != null && user.Password != null) {
            try {
                result = search.FindOne();
                if (result != null) {
                    loginattempt = "Success";
                    userisauthenticated = true;
                }
            } catch {
                loginattempt = "Wrong username or password";
            }
        }
    }

I'd appreciate guidance on how to integrate these two parts.


Great question! Let's break down how to properly wire up your LDAP logic with the AuthenticationStateProvider step by step:

1. Fix the GetAuthenticationStateAsync method

Right now, your method doesn't return a valid Task<AuthenticationState> — that's a critical gap. This method needs to return the current authentication state at all times:

  • For unauthenticated users, return an empty ClaimsPrincipal
  • For authenticated users, return a principal built from the LDAP user's data

2. Expose a public login method to trigger LDAP validation

Your private dothething method should be converted to a public, async method that your UI (like a login page) can call. This method will handle the LDAP check, and if successful, update the authentication state.

3. Notify the framework of state changes

After a login (success or failure), you must call NotifyAuthenticationStateChanged to let ASP.NET/Blazor know the user's authentication status has changed. This triggers updates across all components that rely on AuthenticationState.

4. Secure your LDAP filter

Your current filter uses string concatenation, which opens you up to LDAP injection attacks. Use a properly formatted filter instead.

Full corrected example code

Here's how your Login_Logic class should look after these adjustments:

using System.Security.Claims;
using System.DirectoryServices;
using System.Threading.Tasks;
using System.Collections.Generic;

public class Login_Logic : AuthenticationStateProvider
{
    private ClaimsPrincipal _currentUser = new ClaimsPrincipal(new ClaimsIdentity());

    public string LoginAttemptStatus { get; private set; }
    public bool IsUserAuthenticated => _currentUser.Identity.IsAuthenticated;

    public override Task<AuthenticationState> GetAuthenticationStateAsync()
    {
        return Task.FromResult(new AuthenticationState(_currentUser));
    }

    public async Task<bool> Login(UserData user)
    {
        if (string.IsNullOrEmpty(user.UserName) || string.IsNullOrEmpty(user.Password))
        {
            LoginAttemptStatus = "Username and password are required";
            return false;
        }

        try
        {
            // Validate credentials by attempting to bind to the LDAP server
            using var entry = new DirectoryEntry("LDAP://myldap", user.UserName, user.Password);
            // Accessing NativeObject forces credential validation
            _ = entry.NativeObject;

            // Fetch additional user details if credentials are valid
            using var searcher = new DirectorySearcher(entry)
            {
                Filter = $"(&(objectClass=user)(SAMAccountName={user.UserName}))",
                PropertiesToLoad = { "displayName", "mail", "telephoneNumber" }
            };

            var searchResult = searcher.FindOne();
            if (searchResult == null)
            {
                LoginAttemptStatus = "User not found in LDAP directory";
                return false;
            }

            // Build claims from LDAP properties
            var claims = new List<Claim>
            {
                new Claim(ClaimTypes.Name, searchResult.Properties["displayName"][0]?.ToString() ?? user.UserName),
                new Claim(ClaimTypes.NameIdentifier, user.UserName),
                new Claim(ClaimTypes.Email, searchResult.Properties["mail"][0]?.ToString() ?? string.Empty),
                new Claim("Phone", searchResult.Properties["telephoneNumber"][0]?.ToString() ?? string.Empty)
            };

            // Create authenticated identity and update current user
            var identity = new ClaimsIdentity(claims, "LDAPAuthentication");
            _currentUser = new ClaimsPrincipal(identity);

            // Notify the framework that authentication state has changed
            NotifyAuthenticationStateChanged(GetAuthenticationStateAsync());

            LoginAttemptStatus = "Login successful";
            return true;
        }
        catch (DirectoryServicesCOMException ex)
        {
            // Handle specific LDAP error codes for better user feedback
            LoginAttemptStatus = ex.ErrorCode switch
            {
                -2147023570 => "Invalid username or password",
                -2147024891 => "Account locked or disabled",
                _ => $"Login failed: {ex.Message}"
            };
            return false;
        }
        catch (Exception ex)
        {
            LoginAttemptStatus = $"Unexpected error: {ex.Message}";
            return false;
        }
    }

    public void Logout()
    {
        // Reset to unauthenticated state
        _currentUser = new ClaimsPrincipal(new ClaimsIdentity());
        NotifyAuthenticationStateChanged(GetAuthenticationStateAsync());
        LoginAttemptStatus = "Logged out successfully";
    }
}

Key usage tips:

  • Call Login from your UI: Inject Login_Logic into your login component, then call await Login(userData) when the user submits their credentials.
  • Display feedback: Use the LoginAttemptStatus property to show success/error messages to the user.
  • Adjust LDAP settings: Update the LDAP://myldap path to match your actual LDAP server (e.g., LDAP://yourdomain.com for Active Directory).
  • Define UserData: Make sure your UserData class has UserName and Password properties to pass into the Login method.

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.27 21:17:41