Подтвердить что ты не робот

.net Forms Authentication - ручная настройка HttpContext.Current.User не работает в пользовательском AuthorizeAttribute

Я бил это один в течение нескольких часов, и я в тупике. Я делаю запрос на отправку ajax контроллеру MVC 5, пытаясь автоматически войти в систему с определенным предопределенным "супер" пользователем. В методе контроллера я пытаюсь программно установить HttpContext.Current.User и аутентифицироваться, поэтому суперпользователь может пропустить процесс ручного входа в систему. Консенсус по этому вопросу, похоже, здесь, который я реализовал:

настройка HttpContext.Current.User

Кажется, что это работает, пока я не попытаюсь просмотреть любые другие методы контроллера с помощью специального атрибута AuthorizeAttribute.

Метод контроллера:

[HttpPost]
[AllowAnonymous]
public ActionResult Login(string username)
{
    string password = ConfigurationManager.AppSettings["Pass"];

    User user = service.Login(username, password);

    var name = FormsAuthentication.FormsCookieName;
    var cookie = Response.Cookies[name]; 
    if (cookie != null)
    {   
        var ticket = FormsAuthentication.Decrypt(cookie.Value);
        if (ticket != null && !ticket.Expired)
        {
            string[] roles = (ticket.UserData as string ?? "").Split(',');
            System.Web.HttpContext.Current.User = new GenericPrincipal(new FormsIdentity(ticket), roles);
        }
    }

    //...processing result

    return Json(result);
}

Метод service.Login выше создает файл cookie:

FormsAuthentication.SetAuthCookie(cookieValue, false);

Хотя я устанавливаю User, у которого есть Identity и IsAuthenticated, верно, filterContext.HttpContext.User ниже - это не тот же пользователь. Он по существу пуст, как будто он никогда не был назначен и не аутентифицирован.

public override void OnAuthorization(AuthorizationContext filterContext) 
{
    string[] userDetails = filterContext.HttpContext.User.Identity.Name.Split(char.Parse("|"));
}

Ближайший пост, который я мог найти, находится здесь: IsAuthenticated работает в браузере - но не с клиентом Air!

Однако исправление для этого уже было для меня:

<authentication mode="Forms">
  <forms cookieless="UseCookies" timeout="60" loginUrl="~/Account/Login" />
</authentication>

Что мне не хватает, чтобы заставить AuthorizationContext.User соответствовать HttpContext.Current.User, который я аутентифицирую в контроллере?

UPDATE:

Я понимаю, что мне нужно перенаправить, чтобы настроить cookie правильно, я просто не могу сделать эту работу удаленно, используя вызов ajax. Вот что выглядит script на сайте A при выполнении метода контроллера на сайте B. Это перенаправление не устанавливает сеанс. Этого еще нет при аутентификации пользователя по следующему методу контроллера. Он просто перенаправляет меня обратно в окно входа.

function remoteLogin(id) {
    $.ajax({
        url: "/MyController/RemoteLogin",
        type: "POST",
        dataType: "json",
        data: { "id": id }
    }).done(function (data) {
        if (data) {
            if (data.user) {
                var user = data.user;
                $.ajax({
                    url: "http://siteB.xyz/Account/Login",
                    type: "POST",
                    dataType: "json",
                    data: { "username": user.username, "password": user.password }
                }).done(function (data) {
                    if (data) {
                        window.location.href = "http://siteB.xyz/Next"
                    } else {
                        alert("Fail.");
                    }
                }).fail(function (data) {
                    alert("Fail.");
                });
            } else {
                alert("Fail.");
            }
        }
    }).fail(function (data) {
        alert("Fail.");
    });
}
4b9b3361

Ответ 1

Проблема заключается в том, что вы устанавливаете только cookie проверки подлинности, IPrincipal, который создается внутри модуля проверки подлинности форм, не будет выполняться до тех пор, пока не появится новый запрос - поэтому в этот момент HttpContext.User находится в странное состояние. Как только перенаправление произойдет тогда, потому что это новый запрос от браузера, cookie будет прочитан до того, как ваша страница будет достигнута, и создаст правильный пользовательский объект.

Cookies устанавливаются только в браузере после завершения запроса.

Ответ 2

Проблема связана с тем, где работает ваш код действия относительно конвейера обработки запросов. Ваш код работает на шаге ProcessRequest (см. Ниже).

Ожидается, что HttpContext.Current.User будет установлен обработчиком события AuthenticateRequest. FormsAuthenticationModule позаботится об этом, превратив Request.Cookies ' FormsAuthenticationCookie обратно в FormsAuthenticationTicket, а затем в IPrincipal. Этот Принцип получает значение Request.CurrentUser, и я считаю Thread.CurrentPrincipal

Это нужно сделать на шаге AuthenticateRequest, потому что кеш запросов может отличаться от пользователя (ResolveRequestCache), и состояние сеанса всегда выполняется ( AcquireRequestState > ). Кроме того, шаг AuthorizeRequest принимает решения о том, имеет ли пользовательский набор, назначенный на шаге AuthenticateRequest, разрешение, необходимое для прохождения через AuthorizeRequest, без получения перенаправление 401 или 300 уровней на страницу входа (и я считаю, что механизмы авторизации MVC и WebAPI работают как часть ProcessRequest)

  • BeginRequest
  • AuthenticateRequest (FormsAuthenticationModule)
  • AuthorizeRequest (например, UrlAuthorizationModule)
  • ResolveRequestCache
  • MapRequestHandler
  • AcquireRequestState
  • PreRequestHandlerExecute
  • ProcessRequest (HttpHandler, веб-формы, действия контроллера MVC живут здесь)

Вы можете попытаться сделать это, установив HttpContext.Current.User и Thread.CurrentPrincipal на IPrincipal, но это применимо только для запуска кода после этой точки на этапе ProcessRequest... важные решения о состоянии сеанса, кеше и авторизации уже сделаны.

В теории вы можете реализовать нечто похожее на то, что вы пытаетесь сделать, написав HttpModule и внедряя его в /before AuthenticateRequest (или global.asax), но вы еще не успели имеют доступ к концепциям контроллера MVC более высокого уровня, состоянию сеанса и т.д. Вы сможете проверить HttpContext.Request.QueryString, HttpContext.Request.Form и HttpContext.Request.Cookies, но не намного больше. Вам нужно будет захватить имя пользователя из вашего запроса AJAX из HTTP-запроса и либо вызвать FormsAuthentication.SetAuthCookie(), либо создать FormsAuthenticationTicket и FormsAuthenticationCookie и напишите cookie в Request.Cookies и Response.Cookies. К тому моменту, когда логика контроллера работает, я уверен, что запрос будет аутентифицирован.