ASP.NET MVC Forms Authentication несколько одновременных входов - PullRequest
4 голосов
/ 24 марта 2012

Мой сценарий, вероятно, противоположен большинству, я хочу РАЗРЕШИТЬ несколько одновременных входов в систему, но только для разных типов пользователей.

  • Пользователь - имеет свою собственную область
  • Администратор -Имеет свою собственную область

Проблема возникает из-за того, что администратор может быть также и пользователем (у них есть две учетные записи, это в основном так, что они могут проверить, как система работает от пользователя PoV) и хотятчтобы войти в оба одновременно.

При проверке подлинности с помощью форм это не представляется возможным.Поэтому мне пришлось немного «взломать» его и беспокоиться, что я мог что-то упустить.

План:

  • Два фильтра действий для каждого типа пользователя: UserAuthorise & AdminAuthorise
  • Наличие двух файлов cookie сеанса для каждого типа пользователя.
  • Украсьте контроллеры, для которых правильный фильтр действий основан на том, какой пользователь может получить к нему доступ.

Код может нуждаться в некоторой очисткеup.

Я также сделаю имена файлов 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, и вы не можете получить доступ к области типа пользователя без входа в систему.

Ответы [ 3 ]

3 голосов
/ 25 марта 2012

Во-первых, вы, вероятно, должны быть производными от AuthorizeAttribute, а не ActionFilterAttribute.Фильтры AutorizationFilters работают до ActionFilters и допускают короткое замыкание (то есть, если фильтр авторизации дает сбой, фильтры действий не будут выполняться).Кроме того, ActionFilters связаны друг с другом и могут выполняться в любом порядке.

Во-вторых, не рекомендуется вводить имя пользователя и пароль администратора в атрибут.Пароли действительно должны быть односторонними.

1 голос
/ 25 марта 2012

То, что вам нужно для этого сценария, называется impersonation, в основном все, что вам нужно сделать, - это установить поддельный файл cookie аутентификации с олицетворенными пользовательскими данными (чтобы администратор мог видеть то, что видит клиент).

Возможно, вы также захотите отследить это, чтобы вы могли разместить в пользовательском интерфейсе администратора, подражая пользователю информацию о состоянии приложения, а также дать ему ссылку для завершения сеанса олицетворения (вы могли бы в этот момент восстановите предыдущий файл cookie), вместо того, чтобы позволить ему «войти дважды».

Вы можете проверить это , поскольку оно может содержать некоторые полезные сведения для вас (немного устаревшие, но всегда действительные).

0 голосов
/ 26 марта 2012

Что касается вашей модели базы данных, я бы назначил пользователю несколько ролей (простой пользователь, администратор, администратор и т. Д.).Таким образом, вы могли бы войти в систему только один раз, используя роль по умолчанию (admin), и иметь возможность переключаться на другую роль (простой пользовательский PoV) и сохранять разрешения для сеанса.

...