使用SignalR和传输模式长轮询时,Asp.net会话永不过期

时间:2014-05-31 14:32:21

标签: asp.net internet-explorer session signalr long-polling

我们有一个使用SignalR作为其通知机制的Web应用程序。问题是当我们使用IE浏览我们的Web应用程序时,SignalR使用Long Polling作为其传输类型,因此将请求发送回我们的Web服务器,因此Session永不过期无论浏览器闲置多长时间。

我们认为也许我们可以在Global.asax中捕获请求并查看它们是否来自SingalR并将会话超时设置为剩余时间(我不认为它是一个简单的解决方案)

我们缺少其他解决方案吗?

4 个答案:

答案 0 :(得分:11)

我目前使用的解决方法是IHttpModule来检查请求是否是Signalr请求,如果是这样,删除身份验证cookie,这将阻止ASP.net会话超时被重置,所以如果你的会话超时是20分钟,唯一的请求是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客户端轮询端点以获取数据时。

答案 1 :(得分:3)

如果你看一下我刚才写的description of the SignalR protocol,你会发现:

»ping - ping服务器 ... 备注:ping请求实际上不是“连接管理请求”。 此请求的唯一目的是使ASP.NET会话保持活动。它仅由JavaScript客户端发送。

所以,我猜ping请求正在完成它的工作。

答案 2 :(得分:1)

我在这里发布@Simon Mourier评论的解决方案his approval作为CW答案,因为我发现建议的方法最合适且不那么具有干扰性,因为它只是禁用了SignalR请求的会话。

一个积极的副作用是请求将被更快地处理,因为不需要启动和加载Session对象。

它仍然使用IHttpModule进行工作,在使用Session对象之前,最好的地方可能是AcquireRequestState事件(虽然尚未亲自测试),或者在之前引发的事件中。

请注意使用此方法,可能需要在访问任何成员或存储对象之前测试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()
    {
    }
}

这是另一种完全不同的方法,简单但非常有效。

不使用Session / Auth cookie来决定用户是否超时,而是使用Cache对象。这或多或少没有副作用,就像用户只是注销一样。

只需在Web应用程序代码开头的某处添加这个小代码段,当然SignalR不会去,您将能够检查缓存项是否存在并重新启动它(具有相同的到期时间)会话超时已设置),如果没有,只需注销并删除co​​okie /会话变量。

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
            );

答案 3 :(得分:0)

它更稳定和可维护 - 在我看来 - 拥有自己的"会话,如超时" 。将.NET会话超时设置为无穷大,因为您没有使用它,然后创建一个全局JavaScript计数器(在您的布局或母版页中)来跟踪浏览器空闲时的传递时间(显然setTimeoutsetInterval每隔几秒钟就能完成这个伎俩。确保在每个Web请求上重置计数器(这应该会自动发生,因为所有JavaScript变量都会重置)。如果您的页面依赖于Web服务或Web API,请确保在每次调用时重置全局JavaScript计数器。如果计数器在未重置的情况下达到所需的超时,则表示会话已过期,您可以注销该用户。使用此方法,您可以完全控制会话生存期,这使您可以创建注销计时器弹出窗口,以警告用户会话即将过期。 SignalR非常适合这种方法,因为JavaScript计时器会继续滴答作响。