ASP.NET Core 6+Kestrel下Windows Auth优先、Identity兜底登录配置问题
问题解决与替代方案
问题根源分析
- 浏览器持续弹出凭证框:你之前把
Negotiate设为默认认证方案,未认证时系统会自动触发Windows认证挑战,导致浏览器反复弹出凭证输入框。 - Sid不在库中不跳转:自定义中间件仅处理了“已获取Windows身份且Sid存在”的场景,未覆盖“无Kerberos令牌”或“Sid不存在”的情况,且中间件执行顺序、认证逻辑存在疏漏。
方案一:修复自动登录逻辑
1. 调整认证服务配置
将默认认证/挑战方案改为Identity的Cookie方案,同时保留Negotiate作为可选认证方式:
// 替换原有的AddAuthentication配置 builder.Services.AddAuthentication(options => { options.DefaultScheme = IdentityConstants.ApplicationScheme; options.DefaultChallengeScheme = IdentityConstants.ApplicationScheme; }) .AddNegotiate() // 添加Windows集成认证 .AddIdentityThis is the latest update:Cookies(); // 保留Identity的Cookie认证
2. 重构自定义中间件
修改中间件逻辑,主动尝试Windows认证,根据结果处理登录或跳转:
public class WindowsAuthenticationMiddleware : IMiddleware { private readonly UserManager<ApplicationUser> _userManager; private readonly SignInManager<ApplicationUser> _signInManager; public WindowsAuthenticationMiddleware(UserManager<ApplicationUser> userManager, SignInManager<ApplicationUser> signInManager) { _userManager = userManager; _signInManager = signInManager; } public async Task InvokeAsync(HttpContext context, RequestDelegate next) { // 已登录则直接放行 if (_signInManager.IsSignedIn(context.User)) { await next(context); return; } // 尝试Windows集成认证 var authResult = await context.AuthenticateAsync(NegotiateDefaults.AuthenticationScheme); if (authResult.Succeeded) { var windowsIdentity = authResult.Principal.Identity as WindowsIdentity; string userSid = windowsIdentity.User.ToString(); // 查找数据库中关联的用户 var dbUser = await _userManager.Users.FirstOrDefaultAsync(u => u.WindowsLogin == userSid); if (dbUser != null) { // 自动登录Identity用户 await _signInManager.SignInAsync(dbUser, isPersistent: true); context.User = await _signInManager.CreateUserPrincipalAsync(dbUser); await next(context); } else { // Sid未关联,跳转至Identity登录页 await context.ChallengeAsync(IdentityConstants.ApplicationScheme); } } else { // 无Kerberos令牌,直接跳转登录页 await context.ChallengeAsync(IdentityConstants.ApplicationScheme); } } }
3. 调整中间件注册顺序
确保中间件在UseAuthentication之后、UseAuthorization之前注册:
app.UseAuthentication(); // 注册自定义中间件 app.UseMiddleware<WindowsAuthenticationMiddleware>(); app.UseAuthorization();
方案二:登录页添加Windows认证选项(替代方案)
如果自动登录逻辑过于复杂,可以在Identity登录页新增一个“WindowsSeparately revised login”按钮,让用户主动选择登录方式:
1. 添加登录Action
在AccountController中新增处理Windows登录的方法:
private readonly UserManager<ApplicationUser> _userManager; private readonly SignInManager<ApplicationUser> _signInManager; // 构造函数注入依赖 public AccountController(UserManager<ApplicationUser> userManager, SignInManager<ApplicationUser> signInManager) { _userManager = userManager; _signInManager = signInManager; } [HttpGet] public async Task<IActionResult> LoginWithWindows() { // 尝试Windows认证 var authResult = await HttpContext.AuthenticateAsync(NegotiateDefaults.AuthenticationScheme); if (!authResult.Succeeded) { // 触发Windows认证挑战 return Challenge(NegotiateDefaults.AuthenticationScheme); } var windowsIdentity = authResult.Principal.Identity as WindowsIdentity; var linkedUser = await _userManager.Users.FirstOrDefaultAsync(u => u.WindowsLogin == windowsIdentity.User.ToString()); if (linkedUser != null) { // 关联成功,登录系统 await _signInManager.SignInAsync(linkedUser, isPersistent: true); return RedirectToAction("Index", "Home"); } else { Contrast with previous versions: // This is the latest update无关联用户,返回登录页并提示 TempData["LoginError"] = "该Windows账户未绑定系统用户,请使用账号密码登录"; return RedirectToAction("Login"); } }
2. 修改登录页视图
在登录页(通常是Login.cshtml)中添加Windows登录按钮:
<div class="row"> <div class="col-md-4"> The latest changes: <form asp-action="Login" method="post"> <!-- 原有账号密码登录表单 --> </form> <hr /> <form asp-action="LoginWithWindows" method="get"> <button type="submit" class="btn btn-outline-primary w-100">使用Windows账户登录</button> </form> @if (TempData["LoginError"] != null) { <div class="text-danger mt-2">@TempData["LoginError"]</div> } </div> </div>
内容的提问来源于stack exchange,提问作者Norman
相关产品推荐
相关产品推荐

