ASP.NET Core 7 MVC Windows身份认证:替换浏览器登录弹窗为自定义登录表单的配置咨询
没问题,我来帮你理清思路。首先得明确:纯Windows身份认证(依赖NTLM/Kerberos协议)是和浏览器深度绑定的,默认就是弹出系统级的登录弹窗。如果要换成自定义的表单登录,你需要结合Cookie认证,把Windows账号的验证逻辑搬到自己的表单里,或者同时保留两种认证选项(让用户选表单或自动Windows登录)。
下面是具体的实现步骤和代码示例,一步步来:
1. 调整项目的认证配置(Program.cs)
首先要修改Program.cs,把Cookie认证设为默认的挑战方案(这样未授权请求会跳转到你的自定义登录页,而不是弹出系统弹窗),同时保留Windows认证的能力(如果你需要的话)。
var builder = WebApplication.CreateBuilder(args); // 添加MVC服务 builder.Services.AddControllersWithViews(); // 配置认证体系:Cookie认证为主,Windows认证为辅 builder.Services.AddAuthentication(options => { options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme; options.DefaultChallengeScheme = CookieAuthenticationDefaults.AuthenticationScheme; // 未授权时跳转到Cookie的登录页 }) .AddCookie(options => { options.LoginPath = "/Account/Login"; // 指定自定义登录页的路径 options.AccessDeniedPath = "/Account/AccessDenied"; options.ExpireTimeSpan = TimeSpan.FromHours(2); // Cookie有效期,按需调整 }) .AddNegotiate(); // ASP.NET Core 7+推荐用AddNegotiate替代旧的AddWindows,支持NTLM/Kerberos // 添加授权服务 builder.Services.AddAuthorization(); var app = builder.Build(); // 中间件顺序很重要:认证必须在授权之前 if (!app.Environment.IsDevelopment()) { app.UseExceptionHandler("/Home/Error"); app.UseHsts(); } app.UseHttpsRedirection(); app.UseStaticFiles(); app.UseRouting(); // 先启用认证,再启用授权 app.UseAuthentication(); app.UseAuthorization(); app.MapControllerRoute( name: "default", pattern: "{controller=Home}/{action=Index}/{id?}"); app.Run();
2. 创建登录相关的控制器和视图
接下来需要做一个登录表单,收集用户的域/本地账号密码,然后验证其合法性,验证通过后颁发Cookie。
第一步:创建LoginViewModel(用于表单绑定)
在Models文件夹下新建LoginViewModel.cs:
using System.ComponentModel.DataAnnotations; namespace YourAppName.Models; public class LoginViewModel { [Required(ErrorMessage = "请输入用户名")] [Display(Name = "用户名(域账号格式:DOMAIN\\Username 或 Username)")] public string Username { get; set; } = string.Empty; [Required(ErrorMessage = "请输入密码")] [DataType(DataType.Password)] [Display(Name = "密码")] public string Password { get; set; } = string.Empty; [Display(Name = "记住我")] public bool RememberMe { get; set; } }
第二步:创建Account控制器
在Controllers文件夹下新建AccountController.cs,负责处理登录、登出逻辑,以及Windows认证的可选入口:
using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authentication.Cookies; using Microsoft.AspNetCore.Mvc; using System.DirectoryServices.AccountManagement; using System.Security.Claims; using YourAppName.Models; namespace YourAppName.Controllers; public class AccountController : Controller { // 显示登录表单 [HttpGet] public IActionResult Login(string? returnUrl = null) { ViewData["ReturnUrl"] = returnUrl; return View(); } // 处理表单提交的登录请求 [HttpPost] [ValidateAntiForgeryToken] public async Task<IActionResult> Login(LoginViewModel model, string? returnUrl = null) { ViewData["ReturnUrl"] = returnUrl; if (ModelState.IsValid) { // 验证用户的Windows账号密码 bool isAuthenticated = ValidateWindowsCredentials(model.Username, model.Password); if (isAuthenticated) { // 创建用户身份标识(Claims) var claims = new List<Claim> { new Claim(ClaimTypes.Name, model.Username), new Claim(ClaimTypes.NameIdentifier, model.Username) // 可以根据需求添加更多Claims,比如角色、部门等 }; var claimsIdentity = new ClaimsIdentity( claims, CookieAuthenticationDefaults.AuthenticationScheme); var authProperties = new AuthenticationProperties { IsPersistent = model.RememberMe, // 是否持久化Cookie(记住我) RedirectUri = returnUrl ?? Url.Action("Index", "Home") }; // 颁发认证Cookie await HttpContext.SignInAsync( CookieAuthenticationDefaults.AuthenticationScheme, new ClaimsPrincipal(claimsIdentity), authProperties); // 跳转到用户原本要访问的页面 return LocalRedirect(returnUrl ?? Url.Action("Index", "Home")); } else { ModelState.AddModelError(string.Empty, "用户名或密码错误,请重试"); } } // 验证失败,返回登录页 return View(model); } // 登出功能 [HttpPost] [ValidateAntiForgeryToken] public async Task<IActionResult> Logout() { await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme); return RedirectToAction("Index", "Home"); } // 可选:提供Windows自动登录的入口(让用户可以选择用系统账号自动登录) [HttpGet] public IActionResult LoginWithWindows(string? returnUrl = null) { var properties = new AuthenticationProperties { RedirectUri = returnUrl ?? Url.Action("Index", "Home") }; // 触发Windows认证挑战 return Challenge(properties, NegotiateDefaults.AuthenticationScheme); } // 核心:验证Windows账号密码的方法 private bool ValidateWindowsCredentials(string username, string password) { try { // 如果是域账号,用ContextType.Domain;如果是本地机器账号,用ContextType.Machine using var context = new PrincipalContext(ContextType.Domain); // 验证账号密码,支持DOMAIN\\Username 或 Username格式 return context.ValidateCredentials(username, password); } catch (Exception ex) { // 可以在这里记录日志,比如认证失败的异常信息 Console.WriteLine($"验证失败:{ex.Message}"); return false; } } }
第三步:创建登录视图
在Views文件夹下新建Account文件夹,然后新建Login.cshtml:
@{ ViewData["Title"] = "用户登录"; } <div class="container mt-5"> <div class="row justify-content-center"> <div class="col-md-6"> <div class="card"> <div class="card-header"> <h3>@ViewData["Title"]</h3> </div> <div class="card-body"> <form asp-action="Login" method="post"> <input type="hidden" asp-for="@ViewData["ReturnUrl"]" /> <div asp-validation-summary="All" class="text-danger mb-3"></div> <div class="mb-3"> <label asp-for="Username" class="form-label"></label> <input asp-for="Username" class="form-control" placeholder="例如:DOMAIN\\张三 或 张三" /> <span asp-validation-for="Username" class="text-danger"></span> </div> <div class="mb-3"> <label asp-for="Password" class="form-label"></label> <input asp-for="Password" class="form-control" /> <span asp-validation-for="Password" class="text-danger"></span> </div> <div class="mb-3 form-check"> <input asp-for="RememberMe" class="form-check-input" /> <label asp-for="RememberMe" class="form-check-label"></label> </div> <div class="d-grid gap-2"> <button type="submit" class="btn btn-primary">登录</button> <a asp-action="LoginWithWindows" asp-route-returnUrl="@ViewData["ReturnUrl"]" class="btn btn-secondary">使用Windows账号自动登录</a> </div> </form> </div> </div> </div> </div> </div> @section Scripts { @{await Html.RenderPartialAsync("_ValidationScriptsPartial");} }
3. 安装必要的NuGet包
因为用到了System.DirectoryServices.AccountManagement来验证Windows账号,需要安装对应的NuGet包:
打开NuGet包管理器,搜索并安装System.DirectoryServices.AccountManagement。
4. 测试效果
现在启动项目,当访问需要授权的页面时,会自动跳转到你自定义的登录表单,而不是弹出系统登录框。用户输入正确的Windows账号密码后,就能正常访问,后续请求会用Cookie维持登录状态。如果用户选择"使用Windows账号自动登录",则会触发原本的Windows认证流程(自动登录,不需要输入密码,前提是用户已经登录到域/本地机器)。
备注:内容来源于stack exchange,提问作者Anthony Mullen

