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

4

我的情况可能与大多数人相反,我希望允许多个用户同时登录,但仅限于不同类型的用户。

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

问题在于管理员也可以是用户(他们有两个帐户,这主要是为了能够从用户角度检查系统正在运行的情况),并且希望同时登录两个帐户。

使用表单身份验证时,似乎无法做到这一点。所以我不得不“黑客”一下,并担心我可能会忽略某些东西。

计划:

  • 每种类型的用户有两个操作过滤器:UserAuthorise和AdminAuthorise
  • 每种类型的用户有两个会话cookie
  • 根据哪个用户可以访问它,修饰控制器的正确操作过滤器。

代码可能需要进行一些整理。

我还将使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);
    }
}

管理员授权:

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,您无法在未登录情况下访问用户类型区域。

3个回答

3

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

其次,将管理员用户名和密码硬编码到属性中并不是一个好主意。密码应该进行单向哈希处理。


2
是的,为了让我的示例更加简洁明了,我已经删除了用户名/密码哈希/盐值以及从数据库中提取这些信息的部分,只保留了我需要审查的关键部分。 - Phil

1

对于这种情况,您需要的是所谓的“模拟”,基本上您需要做的就是使用模拟用户数据设置一个虚假的身份验证 cookie(以便管理员可以看到客户看到的内容)。

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

您可以查看此处,因为它可能包含对您有用的一些信息(有点旧但始终有效)。


有趣的东西,会记下来加入我的设置,但与我的实际问题和使用场景无关。 - Phil

0
关于您的数据库模型,我会给用户分配几个角色(普通用户、管理员、监管员等)。这样,您只需使用默认角色(管理员)登录一次,并有切换到另一个角色(普通用户视角)并在会话中存储权限的选项。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接