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

Сессия Asp.net никогда не заканчивается при использовании сигнала SignalR и длительного режима транспорта

У нас есть веб-приложение, которое использует SignalR для своего механизма уведомления. Проблема в том, что когда мы просматриваем наше веб-приложение с помощью IE, SignalR использует Long Polling, так как его транспортный тип отправляет запросы на наш веб-сервер, поэтому Session никогда не истекает как долго браузер не работает.

Мы думали, что, возможно, мы могли бы поймать запросы в Global.asax и посмотреть, были ли они из SingalR и установить тайм-аут сеанса на оставшееся время (что я не считаю это простым решением).

Есть ли какое-либо другое решение, которого нам не хватает?

4b9b3361

Ответ 1

Обходной путь, который я использую в настоящее время, - это IHttpModule, чтобы проверить, является ли запрос сигналом, если это удаляет куки файл проверки подлинности, это предотвратит время ожидания сеанса ASP.net reset, поэтому, если ваш сеанс ожидания 20min, и единственными запросами являются Signalr, пользовательский сеанс будет по-прежнему тайм-аутом, и пользователю придется снова войти в систему.

    public class SignalRCookieBypassModule : IHttpModule
    {
        public void Init(HttpApplication application)
        {
            application.PreSendRequestHeaders += OnPreSendRequestHeaders;
        }

        private bool IsSignalrRequest(string path)
        {
            return path.IndexOf("/signalr/", StringComparison.OrdinalIgnoreCase) > -1;
        }

        protected void OnPreSendRequestHeaders(object sender, EventArgs e)
        {
            var httpContext = ((HttpApplication)sender).Context;
            if (IsSignalrRequest(httpContext.Request.Path))
            {
                // Remove auth cooke to avoid sliding expiration renew
                httpContext.Response.Cookies.Remove(DefaultAuthenticationTypes.ApplicationCookie);
            }
        }

        public void Dispose()
        {
        }
    }

Я считаю, что это реальное решение для взлома, поэтому хотелось бы, чтобы другие идеи предотвращали перезапуск сеанса, когда данные передаются клиенту с сервера, или когда клиент javascript проверяет конечную точку для данных.

Ответ 2

Если вы посмотрите на описание протокола SignalR, я написал некоторое время назад, вы найдете следующее:

"ping - пинги сервера ... Замечания: запрос ping на самом деле не является "запросом на управление соединением". Единственная цель этого запроса - сохранить сеанс ASP.NET. Он отправляется только клиентом JavaScript.

Итак, я предполагаю, что запрос ping выполняет свою работу.

Ответ 3

Я здесь, пост @Simon Mourier прокомментировал решение, его одобрение, как ответ CW, поскольку я нахожу предлагаемый подход наиболее подходящим и менее навязчивым, поскольку он просто отключает сеанс для запросов SignalR.

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

Он по-прежнему использует IHttpModule для работы, и предпочтительным местом является, скорее всего, событие AcquireRequestState (пока еще не проверившееся лично), или в случае, которое было создано ранее, прежде чем использовать объект Session.

Обратите внимание, используя этот подход, чтобы можно было проверить, что объект Session доступен до доступа к любому из его элементов или сохраненных объектов.

public class SignalRSessionBypassModule : IHttpModule
{
    public void Init(HttpApplication application)
    {
        application.AcquireRequestState += OnAcquireRequestState;
    }

    private bool IsSignalrRequest(string path)
    {
        return path.IndexOf("/signalr/", StringComparison.OrdinalIgnoreCase) > -1;
    }

    protected void AcquireRequestState(object sender, EventArgs e)
    {
        var httpContext = ((HttpApplication)sender).Context;
        if (IsSignalrRequest(httpContext.Request.Path))
        {
            // Run request with Session disabled
            httpContext.SetSessionStateBehavior(System.Web.SessionState.SessionStateBehavior.Disabled);
        }
    }

    public void Dispose()
    {
    }
}

Ответ 4

Это более стабильный и поддерживаемый, на мой взгляд, свой собственный сеанс вроде тайм-аута. Установите свой тайм-аут сеанса .NET на бесконечность, поскольку вы не будете его использовать, а затем создайте глобальный счетчик JavaScript (в своем макете или главной странице), чтобы отслеживать время, проходящее во время простоя браузера (очевидно, setTimeout или setInterval каждые несколько секунд будет делать трюк). Убедитесь, что счетчик reset для каждого веб-запроса (это должно произойти автоматически, поскольку все переменные JavaScript будут reset). Если у вас есть страницы, зависящие от веб-служб или веб-API, убедитесь, что reset ваш глобальный счетчик JavaScript при каждом вызове. Если счетчик достигнет желаемого тайм-аута без reset, это означает, что срок действия сеанса истек, и вы можете выйти из системы. При таком подходе вы будете иметь полный контроль над временем жизни сеанса, что позволит вам создать всплывающее окно выхода из системы, чтобы предупредить пользователя о завершении сеанса. SignalR отлично подходит для этого подхода, поскольку таймер JavaScript будет продолжать тикать.

Ответ 5

Вот еще один совершенно другой подход, простой, но довольно эффективный.

Вместо того, чтобы полагаться на файлы cookie Session/Auth, чтобы решить, был ли пользователь тайм-аут, используйте объект Cache. Это имеет более или менее побочные эффекты и работает так же, как если бы пользователь просто вышел из системы.

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

if (Request.IsAuthenticated) {

  if (Cache[Context.User.Identity.Name] == null) {

    // Call you logout method here...,
    // or just:
    // - Sign out from auth;
    // - Delete auth cookie
    // - Remove all session vars

  } else {

    // Reinitiate the cache item
    Cache.Insert(Context.User.Identity.Name,
                 "a to you usable value",
                 null,
                 DateTime.Now.AddMinutes(Session.Timeout),
                 Cache.NoSlidingExpiration,
                 CacheItemPriority.Default,
                 null
                );
}

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

// Insert the cache item
Cache.Insert(Context.User.Identity.Name,
             "a to you usable value",
             null,
             DateTime.Now.AddMinutes(Session.Timeout),
             Cache.NoSlidingExpiration,
             CacheItemPriority.Default,
             null
            );