.NET 6角色登录异常:未登录访问Admin页跳转至普通登录页
.NET 6 管理员与用户分离登录跳转异常问题排查与解决
问题描述
在.NET 6中实现管理员与用户分离登录功能,已分别在Admin区域和普通控制器目录下创建同名的AccountController。当前异常:未登录访问用户页面时可正常跳转至/account/login,但未登录访问管理员页面时,却跳转至/account/login而非预期的/admin/account/login。
现有代码
Admin区域AccountController的PostLogin方法
public IActionResult PostLogin(LoginModel model) { UserLoginData user = _accountRepository.GetUserLoginData(model.UserName, model.Password); if (user != null) { HttpContext.Session.SetString("UserName", user.UserName); HttpContext.Session.SetInt32("UserId", user.UserId); var claims = new List<Claim>(); claims.Add(new Claim(ClaimTypes.Name, user.UserName.ToString())); string[] roles = user.Role.RoleName.Split(","); foreach (string role in roles) { claims.Add(new Claim(ClaimTypes.Role, role)); } var identity = new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme); HttpContext.Response.Cookies.Append("Admin", "Admin"); var principal = new ClaimsPrincipal(identity); var props = new AuthenticationProperties(); props.IsPersistent = false; props.ExpiresUtc = DateTime.UtcNow.AddDays(2); HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, principal, props).Wait(); return RedirectToAction("Dashboard", "Home", new { area = "Admin" }); } else { TempData["NotValid"] = "Invalid username or password. Please try again."; Console.WriteLine("Invalid username or password. Please try again."); return RedirectToAction("Login"); } }
普通AccountController的PostLogin方法
public IActionResult PostLogin(UserLoginData model) { UserLoginData user = _accountRepository.GetUserLoginData(model.EmailId, model.Password); if (user != null) { HttpContext.Session.SetString("Email", user.EmailId); HttpContext.Session.SetInt32("usrid", user.UserId); var claims = new List<Claim>(); claims.Add(new Claim(ClaimTypes.Name, user.EmailId.ToString())); string[] roles = { "Customer" }; foreach (string role in roles) { claims.Add(new Claim(ClaimTypes.Role, role)); } HttpContext.Response.Cookies.Append("Customer", "1"); var identity = new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme); var principal = new ClaimsPrincipal(identity); var props = new AuthenticationProperties(); props.IsPersistent = false; props.ExpiresUtc = DateTime.UtcNow.AddDays(2); HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, principal, props).Wait(); return RedirectToAction("products", "home"); } else { TempData["NotValid"] = "Invalid username or password. Please try again."; Console.WriteLine("Invalid username or password. Please try again."); return RedirectToAction("Login"); } }
Program.cs配置
builder.Services.AddSession(options => { options.IdleTimeout = TimeSpan.FromHours(24); options.Cookie.HttpOnly = true; options.Cookie.IsEssential = true; }); builder.Services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme) .AddCookie(options => { options.ExpireTimeSpan = TimeSpan.FromDays(10); options.AccessDeniedPath = new PathString("/admin/account/login"); options.Events = new CookieAuthenticationEvents { OnRedirectToLogin = ctx => { ctx.Response.Redirect(ctx.RedirectUri); return Task.CompletedTask; } }; }) .AddCookie("Customer", options => { options.ExpireTimeSpan = TimeSpan.FromDays(10); options.AccessDeniedPath = new PathString("/account/login"); options.Events = new CookieAuthenticationEvents { OnRedirectToLogin = ctx => { ctx.Response.Redirect(ctx.RedirectUri); return Task.CompletedTask; } }; }); var app = builder.Build(); // Configure the HTTP request pipeline. if (!app.Environment.IsDevelopment()) { app.UseExceptionHandler("/Home/Error"); app.UseHsts(); } app.UseHttpsRedirection(); app.UseStaticFiles(); app.UseRouting(); app.UseAuthentication(); app.UseAuthorization(); app.UseSession(); app.UseEndpoints(endpoints => { endpoints.MapControllerRoute( name: "areas", pattern: "{area:exists}/{controller=Home}/{action=Dashboard}/{id?}" ); endpoints.MapControllerRoute( name: "default", pattern: "{controller=Home}/{action=Index}/{id?}" ); });
问题根源与修复方案
核心问题
- 认证方案混淆:两个登录逻辑都使用默认的
CookieAuthenticationDefaults.AuthenticationScheme,未区分管理员和用户的独立认证方案。 - 跳转路径配置错误:只设置了
AccessDeniedPath(权限不足时跳转),但未登录跳转由LoginPath控制,且未给管理员方案配置正确的LoginPath。 - 授权未绑定对应认证方案:管理员区域控制器未指定使用管理员专属的认证方案,触发了默认方案的跳转逻辑。
修复步骤
1. 调整Program.cs的认证方案配置
给管理员和用户分别配置独立的认证方案,并设置正确的LoginPath:
builder.Services.AddAuthentication() // 管理员专属认证方案 .AddCookie("Admin", options => { options.ExpireTimeSpan = TimeSpan.FromDays(10); options.LoginPath = new PathString("/admin/account/login"); // 未登录跳转路径 options.AccessDeniedPath = new PathString("/admin/account/login"); // 权限不足跳转路径 options.Events = new CookieAuthenticationEvents { OnRedirectToLogin = ctx => { ctx.Response.Redirect(ctx.RedirectUri); return Task.CompletedTask; } }; }) // 用户专属认证方案 .AddCookie("Customer", options => { options.ExpireTimeSpan = TimeSpan.FromDays(10); options.LoginPath = new PathString("/account/login"); options.AccessDeniedPath = new PathString("/account/login"); options.Events = new CookieAuthenticationEvents { OnRedirectToLogin = ctx => { ctx.Response.Redirect(ctx.RedirectUri); return Task.CompletedTask; } }; });
2. 修改管理员登录逻辑,绑定"Admin"认证方案
调整Admin区域AccountController的PostLogin方法:
// 修改ClaimsIdentity的认证方案 var identity = new ClaimsIdentity(claims, "Admin"); // 修改SignInAsync的认证方案 HttpContext.SignInAsync("Admin", principal, props).Wait();
3. 修改用户登录逻辑,绑定"Customer"认证方案
调整普通AccountController的PostLogin方法:
// 修改ClaimsIdentity的认证方案 var identity = new ClaimsIdentity(claims, "Customer"); // 修改SignInAsync的认证方案 HttpContext.SignInAsync("Customer", principal, props).Wait();
4. 给管理员区域控制器绑定授权策略
在Admin区域的所有需要授权的控制器(比如HomeController)上添加特性:
[Area("Admin")] [Authorize(AuthenticationSchemes = "Admin")] public class HomeController : Controller { // 控制器方法逻辑 }
验证效果
完成以上修改后,未登录访问管理员页面时会自动跳转至/admin/account/login,访问用户页面则跳转至/account/login,实现分离登录的跳转逻辑。
内容的提问来源于stack exchange,提问作者PHioNiX
相关产品推荐
相关产品推荐

