Asp.net MVC - Как проверить срок действия сеанса для запроса Ajax

Мы используем вызов Ajax во всем приложении, пытаясь найти глобальное решение для перенаправления на страницу входа, если сеанс уже истек, при попытке выполнить любой запрос Ajax. Я написал следующее решение, воспользовавшись помощью этого сообщения - Обработка тайм-аута сеанса в вызовах ajax

НЕ УВЕРЕН, ПОЧЕМУ В МОЕМ СОБЫТИИ ПО УХОДУ "HandleUnauthorizedRequest" не запускается.

Настраиваемый атрибут:

 [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
    public class CheckSessionExpireAttribute :AuthorizeAttribute
    {
        protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
        {
            if (filterContext.HttpContext.Request.IsAjaxRequest())
            {
                var url = new UrlHelper(filterContext.RequestContext);
                var loginUrl = url.Content("/Default.aspx");

                filterContext.HttpContext.Session.RemoveAll();
                filterContext.HttpContext.Response.StatusCode = 403;
                filterContext.HttpContext.Response.Redirect(loginUrl, false);
                filterContext.Result = new EmptyResult();
            }
            else
            {
                base.HandleUnauthorizedRequest(filterContext);
            }

        }

    }

Использование настраиваемого атрибута выше, как показано в действии контроллера:

 [NoCache]
 [CheckSessionExpire]
 public ActionResult GetSomething()
 {
  }

Вызов AJAX (часть JS):

function GetSomething()
{
   $.ajax({
        cache: false,
        type: "GET",
        async: true,
        url: "/Customer/GetSomething",
        success: function (data) {

        },
        error: function (xhr, ajaxOptions, thrownError) {

        }
}

Настройки аутентификации Web Config:

  <authentication mode="Forms">
      <forms loginUrl="default.aspx" protection="All" timeout="3000" slidingExpiration="true" />
    </authentication>

Я пытаюсь проверить это, удалив приготовление в браузере перед вызовом ajax, но событие «CheckSessionExpireAttribute» не запускается - любая идея, пожалуйста.

Спасибо,

@Павел


person paul sim    schedule 14.09.2017    source источник
comment
Вы удалили .ASPXAUTH cookie перед отправкой запроса?   -  person shakib    schedule 21.09.2017
comment
Вы хотите проверить срок действия сеанса или выход из системы / срок входа истек? Я думаю, вы хотите что-то сделать с запросом AJAX, если пользователь вышел из системы, но я хотел уточнить, поскольку вы используете эти термины как синонимы.   -  person Tommy    schedule 25.09.2017
comment
@Tommy, предполагая, что он говорит об истечении срока входа в систему, чем это будет отличаться от истечения срока действия сеанса?   -  person Tiramonium    schedule 26.09.2017
comment
@Tiramonium Session используется для хранения переменной между запросами конкретного пользователя либо в памяти веб-сервера, либо в каком-либо другом механизме постоянного хранения. Конечному пользователю обычно выдается файл cookie с Session Id. Cookie-файл аутентификации (или токен) обычно представляет собой зашифрованный cookie-файл, в котором хранится время истечения срока выхода из системы и ваш идентификатор пользователя или имя пользователя. Веб-сервер использует это, чтобы убедиться, что вы являетесь аутентифицированным пользователем и авторизованы для сделанного вами запроса. У вас может быть только сеанс, только авторизация, ни то, ни другое. Они полностью независимы друг от друга.   -  person Tommy    schedule 26.09.2017
comment
@Tiramonium - У них также разные области конфигурации / настройки. Я видел, как разработчики случайно позволяли тайм-ауту сеанса составлять 20 минут, а выходу из системы авторизации - часу. Пользователи теряли данные через 20 минут простоя, даже если, например, они все еще находились в системе. Было весело :)   -  person Tommy    schedule 26.09.2017
comment
@Tommy, если я правильно понял, продолжительность аутентификации в MVC устанавливается в файле решения Web.config, который устанавливает срок действия cookie аутентификации. Но где обычно устанавливается тайм-аут сеанса?   -  person Tiramonium    schedule 26.09.2017
comment
Все еще в web.config, просто другой раздел (stackoverflow.com/questions/1205828/). Однако будьте осторожны, потому что в новой структуре Identity вы устанавливаете свои конфигурации для аутентификации в файле startup.cs (stackoverflow.com/questions/27027748/). Наконец, вот обсуждение SO-ответа на эту тему - stackoverflow.com/questions/17812994/   -  person Tommy    schedule 26.09.2017
comment
@ Tommy, Tiramonium - Я хотел добиться того, чтобы сеанс уже истек, и пользователь пытается выполнить любой вызов ajax, тогда событие HandleUnauthorizedRequest будет срабатывать, и оттуда, если это пользователь вызова ajax, будет перенаправлен на страницу входа. Насколько мне известно, поскольку вызов ajax следует той же строке конвейера запроса, за исключением части рендеринга, событие HandleUnauthorizedRequest должно быть запущено. В моем случае, как упоминалось в моем предыдущем комментарии, во время вызова ajax, если сеанс истек, управление напрямую переходит к части ERROR вызова ajax, действие контроллера не вызывается ajax.   -  person paul sim    schedule 27.09.2017
comment
Вы уже отправляете код состояния 403, почему бы вам не использовать его и не перенаправить клиенту. что-то вроде следующего error: function (xhr, ajaxOptions, thrownError) { if(xhr.status === 403){ location.href = '/Default.aspx' } } удалите filterContext.HttpContext.Response.Redirect(loginUrl, false);, который не будет иметь никакого эффекта   -  person Nilesh    schedule 27.09.2017
comment
@ Nilesh - Как упоминалось ранее, событие HandleUnauthorizedRequest не запускается по истечении срока сеанса, поэтому в JS я не получаю 403.   -  person paul sim    schedule 27.09.2017


Ответы (5)


Если я правильно понял вопрос (и даже если я этого не сделал, все равно спасибо, помог мне решить мою собственную ситуацию), вы хотели избежать загрузки страницы входа в элемент, который должен был отображать другое представление через Аякс. Это или получить код состояния исключения / ошибки во время публикации формы Ajax.

Короче говоря, класс аннотации должен будет переопределить 2 метода, а не только HandleUnauthorizedRequest, и он будет перенаправлен на действие JsonResult, которое сгенерирует параметры для вашей функции Ajax, чтобы знать, что делать.

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true, Inherited = true)]
public class SessionTimeoutAttribute : AuthorizeAttribute
{
    public override void OnAuthorization(AuthorizationContext filterContext)
    {
        IPrincipal user = filterContext.HttpContext.User;
        base.OnAuthorization(filterContext);
        if (!user.Identity.IsAuthenticated) {
            HandleUnauthorizedRequest(filterContext);
        }
    }

    protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
    {
        if (filterContext.HttpContext.Request.IsAjaxRequest())
        {
            filterContext.Result = new RedirectToRouteResult(new
            RouteValueDictionary(new { controller = "AccountController", action = "Timeout" }));
        }
    }
}

Затем установите эту аннотацию в своем действии аутентификации, чтобы каждый раз, когда он вызывается, он будет знать, откуда пришел запрос и какой ответ он должен дать.

[AllowAnonymous]
[SessionTimeout]
public ActionResult Login() { }

Затем ваше перенаправленное действие Json:

[AllowAnonymous]
public JsonResult Timeout()
{
    // For you to display an error message when the login page is loaded, in case you want it
    TempData["hasError"] = true;
    TempData["errorMessage"] = "Your session expired, please log-in again.";

    return Json(new
    {
        @timeout = true,
        url = Url.Content("~/AccountController/Login")
    }, JsonRequestBehavior.AllowGet);
}

Затем в вашей клиентской функции (я воспользовался привилегией написать ее как $.get() вместо $.ajax():

$(document).ready(function () {
    $("[data-ajax-render-html]").each(function () {
        var partial = $(this).attr("data-ajax-render-html");
        var obj = $(this);

        $.get(partial, function (data) {
            if (data.timeout) {
                window.location.href = data.url;
            } else {
                obj.replaceWith(data);
            }
        }).fail(function () {
            obj.replaceWith("Error: It wasn't possible to load the element");
        });
    });
});

Эта функция заменяет тег html этим атрибутом data-ajax-render-html, который содержит адрес просмотра, который вы хотите загрузить, но вы можете настроить его загрузку внутри тега, изменив replaceWith для свойства html().

person Tiramonium    schedule 27.09.2017
comment
@ Tiramonium - Спасибо за ваше предложение - я попробую ваше решение и дам вам знать. В моем случае, если сеанс уже истек, и пользователь нажимает любую кнопку, которая выполняет запрос ajax - в этом случае событие OnAuthorization / HandleUnauthorizedRequest не запускается. - person paul sim; 27.09.2017
comment
Просто, если срок действия сеанса уже истек, и пользователь выполняет какой-либо запрос ajax, пользователь должен быть перенаправлен на страницу входа в систему - для достижения этого я хотел иметь настраиваемый атрибут, который будет работать для любого запроса ajax в моем приложении. Для этого я реализовал CheckSessionExpireAttribute, но не знаю, почему событие HandleUnauthorizedRequest не запускается, если сеанс истек. Надеюсь, я смогу сообщить о своей проблеме. - person paul sim; 27.09.2017
comment
@paulsim да, это именно то, на что нацелен мой ответ. Вы помещаете аннотацию в свое действие входа, затем, если оно обнаруживает, что оно было вызвано из действия JsonResult, оно перенаправляет на другое действие JsonResult, которое будет связываться с функцией ajax, которую вы хотите перенаправить всей DOM, а не только элементом ajax. Попробуйте и дайте мне знать, как это сработает для вас. - person Tiramonium; 27.09.2017

Я думаю, что это проблема только на стороне клиента. На веб-сервере вы можете просто использовать классический атрибут Авторизовать над действиями или контроллерами. Это подтвердит, что запрос аутентифицирован (если есть действующий файл cookie аутентификации или заголовок авторизации), и установит HTTP 401, если не аутентифицирован.

Примечание: сеанс будет автоматически воссоздан, если вы не отправите информацию для авторизации в запросе, но запрос не будет авторизован.

Решение

Затем клиент javascript, который вы должны обработать перенаправление (браузеры делают это автоматически, но с помощью ajax вам нужно делать это вручную)

$.ajax({
    type: "GET",
    url: "/Customer/GetSomething",
    statusCode: {
       401: function() {
          // do redirect to your login page
          window.location.href = '/default.aspx'
       }
    }
});
person Luca Corradi    schedule 26.09.2017

Я проверил и протестировал код, выглядит ясно .. проблема в том, что вызов ajax неправильный ..

Я исправляю код Ajax, попробуйте это ..

function GetSomething() {
        $.ajax({
            cache: false,
            type: "GET",
            async: true,
            url: "/Customer/GetSomething",
            success: function (data) {

            },
            error: function (xhr, ajaxOptions, thrownError) {

            }
        });
    }
person Hüseyin Burak Karadag    schedule 21.09.2017
comment
В моем случае событие HandleUnauthorizedRequest не запускается. - person paul sim; 21.09.2017
comment
stackoverflow.com/questions/11088381/ Надеюсь, это поможет вам @ Паулсим - person Suvethan Nantha; 25.09.2017
comment
В моем случае, если сеанс истек (попытка удалить файлы cookie браузера), при отладке метода ajax, видно, что элемент управления напрямую переходит в раздел ошибок метода ajax с ошибкой ниже - ‹html› ‹head› ‹title› Объект перемещен ‹/ title ›‹/Head› ‹body› ‹h2› Объект перемещен в ‹a href=/default.aspx?ReturnUrl=› здесь ‹/a›. ‹/H2› ‹/body› ‹/html› Думаю, именно поэтому mehtod HandleUnauthorizedRequest в настраиваемом атрибуте не запускается. ЛЮБАЯ ИДЕЯ, ПОЖАЛУЙСТА. - person paul sim; 26.09.2017

В HttpContext.Request.IsAjaxRequest ()

Прочтите эту статью о том, почему запрос Ajax может не распознаваться как таковой.

XMLHttpRequest () не распознается как IsAjaxRequest?

Похоже, что существует зависимость от определенного значения заголовка (X-Requested-With) в запросе, чтобы эта функция вернула истину.

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

Но уверены ли вы, что он попадает в эту строку кода? Вы также можете выполнить отладку с помощью точки останова и посмотреть, какие значения установлены .

При сеансе и аутентификации

Авторизация и тайм-аут сеанса не всегда совпадают. Фактически можно предоставить авторизацию на период дольше, чем сеанс, и, если сеанс отсутствует, восстановить его, если они уже авторизованы. Если вы обнаружите, что в сеансе есть что-то, что вы теряете, что не может быть восстановлено, тогда, возможно, вам следует переместить это в другое место или дополнительно сохранить в другом месте.

Время ожидания для файлов cookie проверки подлинности с помощью форм по умолчанию составляет 30 минут. Время ожидания сеанса по умолчанию - 20 минут.

Тайм-аут сеанса в ASP.NET

HandleUnauthorizedRequest не отменяет

person Greg    schedule 26.09.2017

Сожалеем, что: нужное вам решение невозможно. Причина:

  • Чтобы перенаправить пользователя на страницу входа, у нас есть 2 метода: перенаправление на сервере, перенаправление на клиенте.
  • В вашем случае вы используете Ajax, поэтому у нас есть только 1 метод: перенаправление на клиенте (причина в том, что в основном Ajax означает отправку / получение данных на / с сервера. Поэтому невозможно перенаправить на сервер)
  • Затем, чтобы перенаправить на client. Ajax нужна информация с сервера, в которой говорится, что «перенаправить пользователя на страницу входа», в то время как метод сеанса глобальной проверки должен возвращать Redirect («url here»).
  • очевидно, метод сеанса глобальной проверки не может возвращать 2 типа (return Redirect (), return Json, Xml, Object или string)

В конце концов, я предлагаю:

  • Решение 1: не используйте ajax
  • Решение 2: вы можете использовать ajax, но проверьте метод тайм-аута сеанса на сервере, который не является глобальным. Означает, что вы должны использовать несколько реализаций (количество вызовов ajax = количество инструментов)
person chibao    schedule 27.09.2017