【问题标题】:Asp.net session never expires when using SignalR and transport mode long polling使用 SignalR 和传输模式长轮询时,Asp.net 会话永不过期
【发布时间】:2014-07-21 04:40:30
【问题描述】:

我们有一个使用 SignalR 作为通知机制的网络应用程序。问题是当我们使用 IE 浏览我们的网络应用程序时,SignalR 使用长轮询作为其传输类型,因此将请求发送回我们的网络服务器,因此会话永不过期不管浏览器空闲多长时间。

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

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

【问题讨论】:

  • 你解决了这个问题吗?
  • 我应该与我们的开发人员核实,但当时我们认为我们可以使用会话超时。
  • 您能否不覆盖 SignalR 超时值或使用其中一个事件来执行必要的操作(即终止您的会话?) - 检查:asp.net/signalr/overview/guide-to-the-api/…
  • 您使用的是 ASP.NET Identity 吗?我相信 ASP.NET 在会话到期时将 SignalR 调用与实际 Web 请求区分开来,但除非我有更多关于您的项目、ASP.NET 版本以及您如何处理身份验证等各种事情的信息,否则我无法确定发表评论。
  • 您可以编写一个特定的IHttpModule 并将其添加到web.config,当请求来自SignalR 时,将使用Context.SetSessionStateBehavior 方法将其更改为Disabled,类似于这个:abhijitjana.net/2011/01/15/…

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


【解决方案1】:

我在这里发布@Simon Mourier 的评论解决方案,his approval,作为 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()
    {
    }
}

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

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

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

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

【讨论】:

    【解决方案2】:

    在我看来,拥有自己的“像超时一样的会话”更加稳定和可维护。将您的 .NET 会话超时设置为无穷大,因为您不会使用它,然后创建一个全局 JavaScript 计数器(在您的布局或母版页中)以跟踪浏览器空闲时经过的时间(显然是 setTimeoutsetInterval每隔几秒钟就可以解决问题)。确保在每个 Web 请求上都重置计数器(这应该会自动发生,因为所有 JavaScript 变量都会重置)。如果您的页面依赖于 Web 服务或 Web API,请确保在每次调用时重置您的全局 JavaScript 计数器。如果计数器达到您想要的超时而没有被重置,这意味着会话已过期,您可以注销用户。使用这种方法,您将完全控制会话生命周期,这使您能够创建注销计时器弹出窗口以警告用户会话即将到期。 SignalR 非常适合这种方法,因为 JavaScript 计时器会不断计时。

    【讨论】:

    • 我已经玩过这个选项,并且我还使用鼠标移动事件来重置计数器,以防用户在页面上进行交互但没有点击服务器。我在测试时发现的一个问题是,当您拥有移动设备并隐藏浏览器时,我认为 JavaScript 事件会停止触发。此外,由于 JavaScript 是客户端,因此“更容易被破解”,服务器实际上应该是会话到期的最终仲裁者。
    • 据我所知,在这种情况下破解 JavaScript 有点没用。对于使用工具在屏幕上不断移动鼠标的人来说,您也无能为力。至于在移动设备上隐藏浏览器,我猜是谷歌在他们的“计时器”功能中做到了。
    【解决方案3】:

    我目前使用的解决方法是 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()
            {
            }
        }
    

    我觉得这是一个真正的 hack 解决方案,所以我很喜欢其他想法,以防止在数据从服务器推送到客户端时或当 javascript 客户端轮询数据端点时更新会话超时。

    【讨论】:

    • 它只是轮询服务器的javascript客户端吗?你能用某种计时器覆盖它吗?
    • 这个问题和答案是否会给您一些关于如何避免会话更新和/或检测 SignalR 请求的其他想法:stackoverflow.com/questions/22002092/…
    【解决方案4】:

    如果你看看我前一阵子写的description of the SignalR protocol你会发现:

    » ping – ping 服务器 ... 备注:ping 请求并不是真正的“连接管理请求”。 此请求的唯一目的是保持 ASP.NET 会话处于活动状态。它仅由 JavaScript 客户端发送。

    所以,我猜 ping 请求正在发挥作用。

    【讨论】:

    • 我面临的问题我不希望它保持会话活动,我希望会话超时将用户注销,而不是让他们永远登录,我在 IHTTP 中处理这个目前模块并从响应中删除身份验证cookie,但这有点hacky IMO,删除ping修复了该问题,但是来自服务器的任何推送数据也使会话保持活动状态,这也是我们面临的主要问题,我是希望信号器可以处理 ASP 会话超时,因为这对我们来说是一个安全漏洞,回到原始 websockets 是我们下一步的目标
    • 我不确定删除 ping 请求是否会改变任何事情 - 当使用长轮询时,SignalR 默认每 110 秒发送一次轮询请求,我认为这些请求也会延长会话......
    • 我不希望它延长会话,这就是重点。告诉我它已经在做什么不是答案,答案是阻止它延长会话超时,就像我上面的例子
    猜你喜欢
    • 2013-03-20
    • 2012-10-11
    • 2013-11-23
    • 2013-12-11
    • 1970-01-01
    • 2013-05-30
    • 2012-08-01
    • 2013-07-16
    • 1970-01-01
    相关资源
    最近更新 更多