Срок действия сессии ASP.NET MVC - PullRequest
14 голосов
/ 08 апреля 2010

У нас есть внутреннее приложение ASP.NET MVC, которое требует входа в систему. Вход отлично работает и делает то, что ожидается. У нас истечение сеанса 15 минут. Посидев на одной странице в течение этого периода времени, пользователь потерял сеанс. Если они попытаются обновить текущую страницу или перейти на другую, они получат журнал на странице. Мы сохраняем их запрос, чтобы после того, как они вошли в систему, они могут перейти на запрашиваемую страницу. Это прекрасно работает.

Однако моя проблема в том, что на некоторых страницах есть вызовы AJAX. Например, они могут заполнить часть формы, уйти и позволить истечению срока их сессии. Когда они возвращаются, экран все еще отображается. Если они просто заполняют поле (которое будет выполнять вызов AJAX), вызов AJAX вернет страницу входа (внутри любого div, который AJAX должен был просто вернуть фактические результаты). Это выглядит ужасно.

Я думаю, что решение состоит в том, чтобы сделать саму страницу устаревшей (чтобы после завершения сеанса они автоматически возвращались на экран входа в систему без каких-либо действий с их стороны). Тем не менее, мне интересно, есть ли мнения / идеи о том, как лучше всего реализовать это конкретно в отношении передового опыта в ASP.NET MVC.

Обновление:

Так что я пошел дальше и реализовал это в своем OnActionExecuting (согласно предложению Keltex)

  if (!filterContext.HttpContext.User.Identity.IsAuthenticated)
  {
    if (filterContext.HttpContext.Request.IsAjaxRequest())
    {
      filterContext.HttpContext.Response.Write("Invalid session -- please login!");
      filterContext.HttpContext.Response.End();
    }
    else
    {
      ...
    }
  }

Это определенно делает вещи лучше - теперь, даже если у них есть две вкладки (одна с некоторыми вызовами AJAX, которые они могут инициировать), и они явно выйдут из системы на второй вкладке, они сразу получат что-то более разумное, чем куча испорченных данных AJAX.

Я все еще думаю, что я буду осуществлять обратный отсчет Javascript, который предложила womp.

Ответы [ 6 ]

16 голосов
/ 08 апреля 2010

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

за 5 минут до этого, мы выскакиваем модальноедиалоговое окно с надписью "Ты еще там?"с таймером обратного отсчета.Как только таймер достигнет 0:00, мы перенаправим браузер на страницу входа.

Он реализован с минимальным количеством javascript для вычисления времени и таймера, а также с простым обработчиком .ashx, который обновит сеанс.если пользователь нажимает «Я вернулся!»в диалоговом окне до истечения сеанса.Таким образом, если они вернутся вовремя, они могут обновить сеанс без какой-либо навигации.

7 голосов
/ 08 апреля 2010

Я задал похожий вопрос вчера. Вот мое решение:

Изменен атрибут авторизации:

public class OptionalAuthorizeAttribute : AuthorizeAttribute
{
    private class Http403Result : ActionResult
    {
        public override void ExecuteResult(ControllerContext context)
        {
            // Set the response code to 403.
            context.HttpContext.Response.StatusCode = 403;
            context.HttpContext.Response.Write(CTRes.AuthorizationLostPleaseLogOutAndLogInAgainToContinue);
        }
    }

    private readonly bool _authorize;

    public OptionalAuthorizeAttribute()
    {
        _authorize = true;
    }

    //OptionalAuthorize is turned on on base controller class, so it has to be turned off on some controller. 
    //That is why parameter is introduced.
    public OptionalAuthorizeAttribute(bool authorize)
    {
        _authorize = authorize;
    }

    protected override bool AuthorizeCore(HttpContextBase httpContext)
    {
        //When authorize parameter is set to false, not authorization should be performed.
        if (!_authorize)
            return true;

        var result = base.AuthorizeCore(httpContext);

        return result;
    }

    protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
    {
        if (filterContext.RequestContext.HttpContext.Request.IsAjaxRequest())
        {
            //Ajax request doesn't return to login page, it just returns 403 error.
            filterContext.Result = new Http403Result();
        }
        else
            base.HandleUnauthorizedRequest(filterContext);
    }
}

HandleUnauthorizedRequest переопределяется, поэтому возвращает Http403Result при использовании Ajax. Http403Result изменяет StatusCode на 403 и возвращает сообщение пользователю в ответ. В атрибуте есть дополнительная логика (параметр authorize), потому что я включаю [Authorize] в базовом контроллере и отключаю его на некоторых страницах.

Другая важная часть - глобальная обработка этого ответа на стороне клиента. Это то, что я поместил в Site.Master:

<script type="text/javascript">
    $(document).ready(
        function() {
            $("body").ajaxError(
                function(e,request) {
                    if (request.status == 403) {
                        alert(request.responseText);
                        window.location = '/Logout';
                    }
                }
            );
        }
    );
</script>

Я помещаю GLOBAL ajax обработчик ошибок, и когда происходит сбой $.post с ошибкой 403, выдается ответное сообщение, и пользователь перенаправляется на страницу выхода из системы. Теперь мне не нужно обрабатывать ошибку в каждом $.post запросе, потому что он обрабатывается глобально.

Почему 403, а не 401? 401 внутренне обрабатывается платформой MVC (поэтому перенаправление на страницу входа выполняется после неудачной авторизации).

Что вы думаете об этом?

EDIT:

Об отказе от атрибута [Authorize]: [Authorize] - это не только проверка Identity.IsAuthenticated. Он также обрабатывает кэширование страниц (поэтому вы не кэшируете материал, требующий аутентификации) и перенаправление. Нет необходимости копировать этот код.

2 голосов
/ 08 апреля 2010

Вы можете посмотреть на AjaxOptions, которые можно установить в Ajax.BeginForm (). Существует параметр OnBegin, который можно связать с функцией javascript, которая может вызвать метод Controller, чтобы подтвердить, что сеанс все еще действителен, и, если нет, перенаправить на страницу входа с помощью window.location.

1 голос
/ 08 апреля 2010

Частично проблема заключается в том, что вы позволяете фреймворку делать все. Я бы не стал украшать ваш метод AJAX атрибутом [Authorize]. Вместо этого отметьте User.Identity.IsAuthenticated и, если оно возвращает false, создайте осмысленное сообщение об ошибке.

0 голосов
/ 06 мая 2016

Вот как я это сделал ...

В моей базе контроллер

 protected override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        if (!filterContext.HttpContext.User.Identity.IsAuthenticated)
        {
            if (filterContext.HttpContext.Request.IsAjaxRequest())
            {
                filterContext.HttpContext.Response.StatusCode = 403;
                filterContext.HttpContext.Response.Write(SessionTimeout);
                filterContext.HttpContext.Response.End();
            }
        }
    }

Тогда в моем глобальном файле .js

$.ajaxSetup({
error: function (x, status, error) {
    if (x.status == 403) {
        alert("Sorry, your session has expired. Please login again to continue");
        window.location.href = "/Account/Login";
    }
    else {
        alert("An error occurred: " + status + "nError: " + error);
    }
}

}); * +1011 *

Переменная SessionTimeout является строкой нот. Я опустил реализацию для краткости.

0 голосов
/ 21 августа 2013

Мое решение использует один метатег в форме входа и немного Javascript / jQuery.

LogOn.cshtml

<html>
  <head>
    <meta data-name="__loginform__" content="true" />
    ...
  </head>
  ...
</html>

common.js

var Common = {
    IsLoginForm: function (data) {
        var res = false;

        if (data.indexOf("__loginform__") > 0) {
            // Do a meta-test for login form
            var temp =
                $("<div>")
                    .html(data)
                    .find("meta[data-name='__loginform__']")
                    .attr("content");

            res = !!temp;
        }
        return res;
    }
};

Код AJAX

$.get(myUrl, myData, function (serverData) {
    if (Common.IsLoginForm(serverData)) {
        location.reload();
        return;
    }

    // Proceed with filling your placeholder or whatever you do with serverData response
    // ...
});
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...