You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

.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?}"
    );
});

问题根源与修复方案

核心问题

  1. 认证方案混淆:两个登录逻辑都使用默认的CookieAuthenticationDefaults.AuthenticationScheme,未区分管理员和用户的独立认证方案。
  2. 跳转路径配置错误:只设置了AccessDeniedPath(权限不足时跳转),但未登录跳转由LoginPath控制,且未给管理员方案配置正确的LoginPath。
  3. 授权未绑定对应认证方案:管理员区域控制器未指定使用管理员专属的认证方案,触发了默认方案的跳转逻辑。

修复步骤

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.29 22:31:01