Blazor Server对接LDAP实现Cookie认证后浏览器未生成Cookie问题求助
问题根因
你遇到的Cookie不生成的核心原因是Blazor Server的代码运行逻辑导致的:
Blazor Server的组件C#代码是完全运行在服务端的,你在Login.razor.cs中注入HttpClient发起登录接口请求,本质是服务器自己向自己发起了HTTP请求,接口返回的Set-Cookie头只会保存在服务端HttpClient的Cookie容器中,根本不会发送到用户的浏览器,所以浏览器永远不会生成认证Cookie。
除此之外还有几个次要错误:
- 登录请求的URL拼接错误:
$"NavMan.BaseUri}authentication/login"少了左大括号,正确写法应为$"{NavMan.BaseUri}authentication/login"(如果继续用接口方式的话需要修正) Startup.cs中没有注册IHttpContextAccessor,也没有完善Cookie认证的配置项- 绕远路调用自身服务的登录接口,完全没有必要
修复方案
方案1:直接在Blazor登录逻辑中处理认证(推荐)
不需要额外写认证Controller,直接在登录逻辑中完成LDAP校验和Cookie签发:
- 修正
Startup.cs配置
public void ConfigureServices(IServiceCollection services) { services.AddRazorPages(); services.AddServerSideBlazor(); services.AddControllersWithViews().AddRazorRuntimeCompilation(); // 注册IHttpContextAccessor用于获取当前用户的HttpContext services.AddHttpContextAccessor(); // 完善Cookie认证配置 services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme) .AddCookie(options => { options.LoginPath = "/login"; options.Cookie.Name = ".YourAppName.AuthCookie"; options.Cookie.HttpOnly = true; options.Cookie.SameSite = SameSiteMode.Lax; options.ExpireTimeSpan = TimeSpan.FromDays(1); }); } // Configure方法的中间件顺序不用改,你现在的顺序是对的
- 重写
Login.razor.cs的登录逻辑
public partial class Login { public string Username { get; set; } public string Password { get; set; } [Parameter] public string ErrorMessage { get; set; } [Inject] private IHttpContextAccessor HttpContextAccessor { get; set; } [Inject] private NavigationManager NavMan { get; set; } private async Task PerformLoginAsync() { if (string.IsNullOrEmpty(Username) || string.IsNullOrEmpty(Password)) { ErrorMessage = "请输入用户名和密码"; return; } string path = "LDAP://serveraddress.xxx"; try { using DirectoryEntry entry = new(path, Username, Password); using DirectorySearcher searcher = new(entry); searcher.Filter = $"(&(objectclass=user)(objectcategory=person)(samaccountname={Username}))"; var result = searcher.FindOne(); if (result != null) { List<Claim> claims = new(); claims.Add(new Claim(ClaimTypes.Name, Username)); // 处理角色 foreach (var group in result.Properties["memberof"]) { var distinguishedName = new X500DistinguishedName(group.ToString()); var commonNameData = new AsnEncodedData("CN", distinguishedName.RawData); var commonName = commonNameData.Format(false); if (!string.IsNullOrEmpty(commonName)) { claims.Add(new Claim(ClaimTypes.Role, commonName)); } } // 处理邮箱 foreach (var email in result.Properties["mail"]) { claims.Add(new Claim(ClaimTypes.Email, email.ToString())); } var claimsIdentity = new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme); var authProperties = new AuthenticationProperties { AllowRefresh = true, ExpiresUtc = DateTimeOffset.Now.AddDays(1), IsPersistent = true, }; // 直接给当前用户的HttpContext签发Cookie await HttpContextAccessor.HttpContext.SignInAsync( CookieAuthenticationDefaults.AuthenticationScheme, new ClaimsPrincipal(claimsIdentity), authProperties); // 强制刷新页面加载新的Cookie NavMan.NavigateTo("/", true); } else { ErrorMessage = "用户不存在"; } } catch (Exception) { ErrorMessage = "用户名或密码错误"; } } }
- 可以删除不需要的
AuthenticationController相关代码。
方案2:保留认证Controller的实现
如果你希望保留Controller的登录接口,需要让请求从用户浏览器发起,而不是服务端发起:
- 把登录逻辑改成HTML表单提交,或者用JS互操作调用fetch发起POST请求,示例表单写法:
<!-- 修改Login.razor,用原生表单提交 --> <form action="/authentication/login" method="post"> <MudTextField @bind-Value="@Username" T="string" Label="Username" Name="Username"/> <MudTextField @bind-Value="@Password" T="string" Label="Password" Name="Password" InputType="InputType.Password"/> <button type="submit">Sign In</button> </form>
- 对应修改Controller的Login方法,接收表单参数,校验通过后直接重定向到根目录即可。
内容的提问来源于stack exchange,提问作者WoD
相关产品推荐
相关产品推荐

