我正在尝试将身份验证与 .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;
}
}
GPT 的建议不起作用,因为您需要在 program.cs 中添加给定的中间件。下面的代码将在您的登录和注销路径中使用 cookie 来保存用户凭据,然后您将必须使用 GPT 中的代码手动填充用户声明并使用 signInAsync 登录用户
builder.Services.AddAuthentication("Cookies")
.AddCookie("Cookies", options => {
options.LoginPath` = "/userlogin";
options.LogoutPath = "/logout";`
});
完成后,您将能够访问 httpcontext 访问器中的用户