如何将 LDAP 与基于 .NET 8 的 Blazor Web 应用程序(服务器端)集成?

问题描述 投票:0回答:1

我正在尝试将身份验证与 .NET 8 Blazor Web App 的 LDAP 服务器集成。

我的

launchSettings.json

{
  "$schema": "http://json.schemastore.org/launchsettings.json",
    "iisSettings": {
      "windowsAuthentication": false,
      "anonymousAuthentication": true,
      "iisExpress": {
        "applicationUrl": "http://localhost:25412",
        "sslPort": 44310,
        "environmentVariables": {
          "UserDomains": "mycorp=LDAP://mycorp.com"
        }
      }
    },
    "profiles": {
      "http": {
        "commandName": "Project",
        "dotnetRunMessages": true,
        "launchBrowser": true,
        "applicationUrl": "http://localhost:5102",
        "environmentVariables": {
          "ASPNETCORE_ENVIRONMENT": "Development",
          "UserDomains": "mycorp=LDAP://mycorp.com"
        }
      },
      "https": {
        "commandName": "Project",
        "dotnetRunMessages": true,
        "launchBrowser": true,
        "applicationUrl": "https://localhost:7038;http://localhost:5102",
        "environmentVariables": {
          "ASPNETCORE_ENVIRONMENT": "Development",
          "UserDomains": "mycorp=LDAP://mycorp.com"
        }
      },
      "IIS Express": {
        "commandName": "IISExpress",
        "launchBrowser": true,
        "environmentVariables": {
          "ASPNETCORE_ENVIRONMENT": "Development",
          "UserDomains": "mycorp=LDAP://mycorp.com"
        }
      }
    }
  }

UserLogin.razor

@page "/userlogin"
@using System.ComponentModel.DataAnnotations
@using System.Text
@using System.DirectoryServices
@using RCBuisinessLogic
@using RCBuisinessLogic.Authentication
@using RCWebApp.Models
@rendermode InteractiveServer
@inject IHttpContextAccessor HttpContextAccessor
@inject NavigationManager NavigationManager
@inject UserInformation UserInformation

<div class="user-login" style="height: 630px;">
    <EditForm Model="@Login" OnSubmit="HandleLogin" FormName="UserLoginForm">
        <DataAnnotationsValidator />
        <ValidationSummary />

        <div class="form-group">
            <label for="UserName">Username:</label>
            <InputText id="UserName" class="form-control" @bind-Value="Login.UserName" />
            <ValidationMessage For="@(() => Login.UserName)" />
        </div>

        <div class="form-group">
            <label for="Password">Password:</label>
            <InputText id="Password" class="form-control" @bind-Value="Login.Password" Type="password" />
            <ValidationMessage For="@(() => Login.Password)" />
        </div>

        <button type="submit" class="btn btn-primary">Login</button>
    </EditForm>
</div>

@code {
    private Login Login { get; set; } = new Login();

    private async Task HandleLogin()
    {
        string userDomains = Environment.GetEnvironmentVariable("UserDomains");
        bool isValidLogin = IsValidLogin("LDAP://mycorp.com", "mycorp", Login.UserName, Login.Password, out string retMessage);
        if (isValidLogin)
        {
            NavigationManager.NavigateTo("/dashboard");
        }
        else
        {
            NavigationManager.NavigateTo("/");
        }
    }

    private bool IsValidLogin(string LDAPPath, string domainName, string userName, string password, out string retMessage)
    {
        bool returnValue = false;
        retMessage = null;

        try
        {
            // encode user input before being used in LDAP query.
            string safeUserName = EscapeLdapSearchFilter(userName);
            var userClaims = HttpContextAccessor.HttpContext?.User?.Claims;
            bool isAuthenticated = HttpContextAccessor.HttpContext?.User?.Identity?.IsAuthenticated ?? false;
          

            string email = HttpContextAccessor.HttpContext?.User?.
            FindFirst(System.Security.Claims.ClaimTypes.Email)?.Value;

            var de = new DirectoryEntry(LDAPPath, userName, password);

            using (var ds = new DirectorySearcher(de) { Filter = "samaccountname=" + safeUserName })
            {
                SearchResult sr = ds.FindOne();
                if (sr == null)
                {
                    retMessage = "Invalid Login.";
                }
                else
                {
                    string userID = UserInformation.GetByName($"{domainName}\\{userName}", email);

                    returnValue = true;
                }
            }
        }
        catch (Exception ex)
        {
            retMessage = $"Error during LDAP login: {ex.Message}";
        }

        return returnValue;
    }

    private static string EscapeLdapSearchFilter(string searchFilter)
    {
        StringBuilder escape = new StringBuilder();
        for (int i = 0; i < searchFilter.Length; i++)
        {
            char current = searchFilter[i];
            switch (current)
            {
                case '\\':
                    escape.Append(@"\5c");
                    break;
                case '*':
                    escape.Append(@"\2a");
                    break;
                case '(':
                    escape.Append(@"\28");
                    break;
                case ')':
                    escape.Append(@"\29");
                    break;
                case '\u0000':
                    escape.Append(@"\00");
                    break;
                case '/':
                    escape.Append(@"\2f");
                    break;
                default:
                    escape.Append(current);
                    break;
            }
        }

        return escape.ToString();
    }
}

上面文件中的以下代码中的 userClaims 为空,isAuthenticated 为 false,电子邮件为空

var userClaims = HttpContextAccessor.HttpContext?.User?.Claims; 
            bool isAuthenticated = HttpContextAccessor.HttpContext?.User?.Identity?.IsAuthenticated ?? false;
          
            string email = HttpContextAccessor.HttpContext?.User?.
            FindFirst(System.Security.Claims.ClaimTypes.Email)?.Value;

我的

Program.cs
它有一些我尝试过的身份验证配置,但它不起作用,所以评论了。

using Microsoft.AspNetCore.Authentication;
using RCBuisinessLogic.Authentication;
using RCBuisinessLogic.DataAccess;
using RCWebApp.Components;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddHttpContextAccessor();
// Register IConfiguration
builder.Services.AddSingleton<IConfiguration>(builder.Configuration);

builder.Services.AddSingleton<BaseDAL>();

// Register AuthenticationConfiguration
builder.Services.AddSingleton<AuthenticationConfiguration>();

// Register UserInformation
builder.Services.AddTransient<UserInformation>();

// Add services to the container.
builder.Services.AddRazorComponents()
    .AddInteractiveServerComponents();

// Add authentication services and configure the custom authentication scheme.
//builder.Services.AddAuthentication(options =>
//{
//    options.DefaultAuthenticateScheme = "LDAP";
//    options.DefaultChallengeScheme = "LDAP";
//})
//.AddScheme<AuthenticationSchemeOptions, LdapAuthenticationHandler>("LDAP", options => { });

// Add authorization services
//builder.Services.AddAuthorization(options =>
//{
//    // You can configure authorization policies here if needed.
//    options.AddPolicy("Authenticated", policy => policy.RequireAuthenticatedUser());
//});

var app = builder.Build();

// Use authentication and authorization before handling routing
app.UseAuthentication();
app.UseAuthorization();

//app.Use(async (context, next) =>
//{
//    var user = context.User;

//    // Only redirect authenticated users if they are NOT trying to access '/userlogin'
//    if (user.Identity.IsAuthenticated && !context.Request.Path.StartsWithSegments("/userlogin"))
//    {
//        // Redirect authenticated users to the dashboard or another page
//        context.Response.Redirect("/dashboard");
//        return;
//    }

//    await next();  // Continue the request pipeline
//});

// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error", createScopeForErrors: true);
    // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
    app.UseHsts();
}

app.UseHttpsRedirection();

app.UseStaticFiles();
app.UseAntiforgery();

app.MapRazorComponents<App>()
    .AddInteractiveServerRenderMode();

app.Run();

尝试了chatgpt建议的这个中间件,但是没有用。


using Microsoft.AspNetCore.Authentication;
using Microsoft.Extensions.Options;
using RCBuisinessLogic.Authentication;
using System.DirectoryServices;
using System.Security.Claims;
using System.Text.Encodings.Web;
using System.Threading.Tasks;

public class LdapAuthenticationHandler : AuthenticationHandler<AuthenticationSchemeOptions>
{
    private readonly UserInformation _userInformation;

    public LdapAuthenticationHandler(
        IOptionsMonitor<AuthenticationSchemeOptions> options,
        ILoggerFactory logger,
        UrlEncoder encoder,
        ISystemClock clock,
        UserInformation userInformation)
        : base(options, logger, encoder, clock)
    {
        _userInformation = userInformation;
    }

    protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
    {
        var username = Context.Request.Form["username"];
        var password = Context.Request.Form["password"];
        string domain = "mycorp.com";  // Adjust to your LDAP domain

        // Perform LDAP authentication here.
        var isAuthenticated = AuthenticateWithLdap(username, password, domain, out var userId);
        if (isAuthenticated)
        {
            // Create claims and set the principal
            var claims = new List<Claim>
            {
                new Claim(ClaimTypes.Name, username),
                new Claim(ClaimTypes.NameIdentifier, userId),
                new Claim(ClaimTypes.Email, userId)  // Adjust according to your LDAP setup
            };

            var claimsIdentity = new ClaimsIdentity(claims, "LDAP");
            var claimsPrincipal = new ClaimsPrincipal(claimsIdentity);

            var ticket = new AuthenticationTicket(claimsPrincipal, "LDAP");
            return AuthenticateResult.Success(ticket);
        }

        return AuthenticateResult.Fail("Invalid username or password.");
    }

    private bool AuthenticateWithLdap(string username, string password, string domain, out string userId)
    {
        userId = null;

        try
        {
            var ldapPath = $"LDAP://{domain}";
            using var de = new DirectoryEntry(ldapPath, username, password);
            using var ds = new DirectorySearcher(de)
            {
                Filter = $"(sAMAccountName={username})"
            };

            var result = ds.FindOne();
            if (result != null)
            {
                userId = result.Properties["sAMAccountName"][0].ToString(); // Or other user identifier
                return true;
            }
        }
        catch
        {
            // Log error or handle it
        }

        return false;
    }
}
c# blazor ldap blazor-server-side .net-8.0
1个回答
0
投票

GPT 的建议不起作用,因为您需要在 program.cs 中添加给定的中间件。下面的代码将在您的登录和注销路径中使用 cookie 来保存用户凭据,然后您将必须使用 GPT 中的代码手动填充用户声明并使用 signInAsync 登录用户

builder.Services.AddAuthentication("Cookies")
.AddCookie("Cookies", options => {
                      options.LoginPath` = "/userlogin";
                      options.LogoutPath = "/logout";`
         });

完成后,您将能够访问 httpcontext 访问器中的用户

© www.soinside.com 2019 - 2024. All rights reserved.