ASP.NET MVC Forms身份验证多个同时登录

本文关键字:登录 身份验证 NET MVC Forms ASP | 更新日期: 2023-09-27 18:28:48

我的场景可能与大多数情况相反,我希望允许多个同时登录,但只针对不同类型的用户。

  • 用户--有自己的区域
  • 管理员--有自己的区域

当管理员也可以是用户(他们有两个帐户,这主要是为了让他们可以从用户PoV检查系统的工作情况)并希望同时登录到这两个帐户时,就会出现问题。

使用Forms身份验证,这似乎是不可能的。所以我不得不稍微"破解"一下,担心自己可能忽略了什么。

计划:

  • 每种类型的用户都有两个操作过滤器:UserAuthorise&AdminAuthorise
  • 每种类型的用户都有两个会话cookie
  • 根据什么来装饰正确动作过滤器的控制器用户可以访问它

代码可能需要一些整理。

我也会让饼干的名字更加独特。

排除了视图/路线等内容,因为它们似乎不相关。

留下了样本中的密码盐析/散列,并坚持使用测试值。

UserAuthorise:

public class UserAuthorize : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        var authCookie = filterContext.RequestContext.HttpContext.Request.Cookies["User"];
        if (authCookie == null || authCookie.Value == "")
        {
            filterContext.HttpContext.Response.Redirect("/login");
            base.OnActionExecuting(filterContext);
            return;
        }
        FormsAuthenticationTicket authTicket;
        try
        {
            authTicket = FormsAuthentication.Decrypt(authCookie.Value);
        }
        catch
        {
            filterContext.HttpContext.Response.Redirect("/login");
            base.OnActionExecuting(filterContext);
            return;
        }
        if (authTicket.Expired || authTicket.Expiration <= DateTime.Now)
        {
            filterContext.HttpContext.Response.Redirect("/login");
        }
        base.OnActionExecuting(filterContext);
    }
}

AdminAuthorise:

public class AdminAuthorise : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        var authCookie = filterContext.RequestContext.HttpContext.Request.Cookies["Admin"];
        if (authCookie == null || authCookie.Value == "")
        {
            filterContext.HttpContext.Response.Redirect("/admin/login");
            base.OnActionExecuting(filterContext);
            return;
        }
        FormsAuthenticationTicket authTicket;
        try
        {
            authTicket = FormsAuthentication.Decrypt(authCookie.Value);
        }
        catch
        {
            filterContext.HttpContext.Response.Redirect("/admin/login");
            base.OnActionExecuting(filterContext);
            return;
        }
        if (authTicket.Expired || authTicket.Expiration <= DateTime.Now)
        {
            filterContext.HttpContext.Response.Redirect("/admin/login");
        }
        base.OnActionExecuting(filterContext);
    }
}

用户登录控制器操作:

[HttpPost]
public virtual ActionResult Login(FormCollection form)
{
    if (form["username"] == "admin" && form["password"] == "pass")
    {
        var authTicket = new FormsAuthenticationTicket(
                            1,                             // version
                            form["username"],              // user name
                            DateTime.Now,                  // created
                            DateTime.Now.AddMinutes(20),   // expires
                            false,                         // persistent?
                            ""                             // can be used to store roles
                            );
        string encryptedTicket = FormsAuthentication.Encrypt(authTicket);
        var authCookie = new HttpCookie("User", encryptedTicket);
        Response.Cookies.Add(authCookie);
        // Redirect back to the page you were trying to access
        return RedirectToAction(MVC.Home.Index());
    }
    else
    {
        ModelState.AddModelError("", "Bad info mate");
    }
    return View();
}

管理员登录控制器操作:

[HttpPost]
public virtual ActionResult Login(FormCollection form)
{
    if (form["username"] == "admin" && form["password"] == "pass")
    {
        var authTicket = new FormsAuthenticationTicket(
                            1,                             // version
                            form["username"],              // user name
                            DateTime.Now,                  // created
                            DateTime.Now.AddMinutes(20),   // expires
                            false,                         // persistent?
                            ""                             // can be used to store roles
                            );

        string encryptedTicket = FormsAuthentication.Encrypt(authTicket);
        var authCookie = new HttpCookie("Admin", encryptedTicket);
        Response.Cookies.Add(authCookie);
        // Redirect back to the page you were trying to access
        return RedirectToAction(MVC.Admin.Home.Index());
    }
    else
    {
        ModelState.AddModelError("", "Bad info mate");
    }
    return View();
}

这一切看起来明智和安全吗?

在FireFox的页面信息窗口中查看cookie,我发现每个用户类型都有自己的cookie,如果不登录,就无法访问用户类型区域

首先,您可能应该从AuthorizeAttribute而不是ActionFilterAttribute派生。AutorizationFilters在ActionFilters之前执行,并允许短路(即,如果授权过滤器失败,则永远不会执行操作过滤器)。此外,ActionFilters是链接在一起的,可以按任何顺序执行。

其次,将管理员用户名和密码硬编码到属性中不是一个好主意。密码真的应该是单向散列的。

ASP.NET MVC Forms身份验证多个同时登录

这个场景需要的是impersonation,基本上你所要做的就是用模拟的用户数据设置一个假的身份验证cookie(这样管理员就可以看到客户看到的内容)。

您可能还想跟踪这一点,这样您就可以在管理员的用户界面上模拟有关应用程序状态的用户信息,并为其提供一个结束模拟会话的链接(此时您将恢复以前的cookie),而不是让他"登录两次"。

你可以查看一下,因为它可能包含一些有用的信息(有点旧,但总是有效的)。

关于数据库模型,我会为一个用户分配几个角色(简单的用户、管理员、主管等)。这样,您就可以使用默认角色(admin)登录一次,并可以选择切换到另一个角色(简单用户PoV)并存储会话权限。