.NET8 MVC中SignInManager.PasswordSignInAsync成功后用户未登录
问题描述
使用.NET 8 MVC开发时遇到以下问题:调用SignInManager.PasswordSignInAsync方法返回登录成功,但用户实际并未登录:
- 登录后本该显示导航栏,却被重定向到登录页
- 访问带有
[Authorize]特性的AdminController时也会被重定向
调试发现:
_Layout.cshtml中的@if(SignInManager.IsSignedIn(User))判断始终为false- 在
HomeController的Login方法中,通过CreateUserPrincipalAsync生成的用户主体验证IsSignedIn却为true
相关代码如下:
Program.cs
builder.Services.AddDefaultIdentity<User>() .AddRoles<IdentityRole<int>>() .AddEntityFrameworkStores<MyDBContext>(); builder.Services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme) .AddCookie(opts => { opts.LoginPath = "/Home/Login"; opts.LogoutPath = "/Home/Login"; opts.ExpireTimeSpan = TimeSpan.FromMinutes(30); opts.AccessDeniedPath = "/Home/Unauthorized"; opts.SlidingExpiration = true; }); var app = builder.Build(); // Configure the HTTP request pipeline. if (!app.Environment.IsDevelopment()) { app.UseExceptionHandler("/Home/Error"); // 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.UseRouting(); app.UseAuthentication(); app.UseAuthorization(); app.MapControllerRoute( name: "default", pattern: "{controller=Home}/{action=Login}/{id?}"); app.Run();
HomeController.cs
public class HomeController : Controller { private readonly ILogger<HomeController> _logger; private readonly SignInManager<User> _signInManager; public HomeController(ILogger<HomeController> logger, SignInManager<User> signInManager) { _logger = logger; _signInManager = signInManager; } [HttpPost] public async Task<IActionResult> Login(LoginViewModel loginCreds) { if(!ModelState.IsValid) return View(loginCreds); _logger.LogInformation("Trying to sign in user"); var result = await _signInManager.PasswordSignInAsync(loginCreds.Email, loginCreds.Password, false, false); if(!result.Succeeded) { _logger.LogInformation("Failed attempt"); ModelState.AddModelError(string.Empty, "Hmm something went wrong!"); return View(loginCreds); } // 调试代码 var user = await _userManager.FindByEmailAsync(loginCreds.Email); var userPrincipals = await _signInManager.CreateUserPrincipalAsync(user); // Boolean comes out true bool signdIn = _signInManager.IsSignedIn(userPrincipals); _logger.LogInformation("Succeeded " + signdIn); _logger.LogInformation("Succeeded"); return RedirectToAction("NewClient", "Admin"); } public IActionResult Login() { return View(); } [HttpPost] public async Task<IActionResult> Logout() { await _signInManager.SignOutAsync(); return RedirectToAction("Login"); } }
_Layout.cshtml
@using Microsoft.AspNetCore.Identity @inject SignInManager<User> SignInManager <body> // Why does this come back false when user is signed in @if(SignInManager.IsSignedIn(User)) { <header> <nav class="navbar navbar-expand-sm navbar-toggleable-sm navbar-light bg-white border-bottom box-shadow mb-3"> <div class="container-fluid"> <a class="navbar-brand" asp-area="" asp-controller="Admin" asp-action="Dashboard">SuperAnchor</a> <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target=".navbar-collapse" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation"> <span class="navbar-toggler-icon"></span> </button> <div class="navbar-collapse collapse d-sm-inline-flex justify-content-between"> <ul class="navbar-nav flex-grow-1"> <li class="nav-item"> <a class="nav-link text-dark" asp-area="" asp-controller="Admin" asp-action="Dashboard">Home</a> </li> @* <li class="nav-item"> <a class="nav-link text-dark" asp-area="" asp-controller="Admin" asp-action="TestPage">Test Page</a> @Html.ActionLink("Test Link Page", "TestPage", "Admin", null, new { @class="nav-link text-dark" }) </li> *@ <li class="nav-item"> <a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="Privacy">Privacy</a> </li> @if(User.IsInRole("Admin")) { <li class="nav-item"> <a class="nav-link text-dark" asp-area="" asp-controller="Admin" asp-action="NewClient">Add new Client</a> </li> <li class="nav-item"> <a class="nav-link text-dark" asp-area="" asp-controller="Admin" asp-action="AddProduct">Add new Product</a> </li> } </ul> </div> @await Html.PartialAsync("_LoginPartial") </div> </nav> </header> } <div class="container"> <main role="main" class="pb-3"> @* @await Html. *@ @RenderBody() </main> </div> </body> </html>
AdminController.cs
[Authorize] public class AdminController : Controller { private readonly ILogger<AdminController> _logger; private readonly UserManager<User> _userManager; private readonly RoleManager<IdentityRole<int>> _roleManager; public AdminController(ILogger<AdminController> logger, UserManager<User> userManager, RoleManager<IdentityRole<int>> roleManager) { _logger = logger; _userManager = userManager; _roleManager = roleManager; } public IActionResult NewClient() { return View(); } }
问题原因及解决方法
核心问题是重复配置了认证系统:AddDefaultIdentity已经自动为Identity框架配置了Cookie认证,包括默认的Cookie名称、认证Scheme等。手动调用AddAuthentication().AddCookie()会覆盖这些默认配置,导致SignInManager生成的认证Cookie无法被后续请求的认证中间件识别,从而出现"登录成功但实际未登录"的现象。
具体修复步骤:
- 移除重复的认证配置:删除
Program.cs中以下代码段:builder.Services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme) .AddCookie(opts => { opts.LoginPath = "/Home/Login"; opts.LogoutPath = "/Home/Login"; opts.ExpireTimeSpan = TimeSpan.FromMinutes(30); opts.AccessDeniedPath = "/Home/Unauthorized"; opts.SlidingExpiration = true; }); - 自定义Identity Cookie配置(可选):如果需要修改Cookie的参数,直接通过
AddDefaultIdentity的扩展方法配置,不要单独添加Cookie认证:builder.Services.AddDefaultIdentity<User>() .AddRoles<IdentityRole<int>>() .AddEntityFrameworkStores<MyDBContext>() .AddCookie(options => { options.LoginPath = "/Home/Login"; options.LogoutPath = "/Home/Login"; options.ExpireTimeSpan = TimeSpan.FromMinutes(30); options.AccessDeniedPath = "/Home/Unauthorized"; options.SlidingExpiration = true; }); - 补充HomeController的依赖注入:Login方法中使用了
_userManager但构造函数未注入,需要补充:private readonly UserManager<User> _userManager; public HomeController(ILogger<HomeController> logger, SignInManager<User> signInManager, UserManager<User> userManager) { _logger = logger; _signInManager = signInManager; _userManager = userManager; } - 验证中间件顺序:确保
UseAuthentication()和UseAuthorization()在UseRouting()之后,当前代码中的顺序是正确的,无需调整。
内容的提问来源于stack exchange,提问作者DonDavid12
相关产品推荐
相关产品推荐

