【问题标题】:Cookies and Sessions Expiration in .NET.NET 中的 Cookie 和会话过期
【发布时间】:2015-03-23 22:35:49
【问题描述】:

我有一个 MVC4 单一应用程序页面。在登录页面有 3 个字段:用户、密码和“记住我”复选框。 C#登录代码是这样的:

   if (WebSecurity.Login(model.UserName, model.Password, persistCookie: model.RememberMe))
   {
       FormsAuthentication.SetAuthCookie(model.UserName, model.RememberMe);
       return Json(new { success = true, redirect = returnUrl });
   }
   else
   {
       ModelState.AddModelError("", "The user name or password provided is incorrect.");
   }

我想这样做:

  • 如果用户使用“记住我”true 登录 - cookie 将一直保留到用户注销。

  • 如果用户使用“记住我”false登录 - cookie 将在 3 小时后过期。

在 web.config 我有这个代码:

<authentication mode="Forms">
      <forms loginUrl="~/" timeout="180" cookieless="UseCookies" slidingExpiration="false" />
</authentication>
<sessionState timeout="180"  />

问题是当用户在短时间内(通常为 10-30 分钟)没有触摸页面,然后用户尝试在页面中执行某些操作 - 出现错误

“此请求的授权已被拒绝。”

(虽然sessionStation超过30分钟!)

用户刷新页面后 - 如果 cookie 尚未过期 - 一切正常。但是我当然不想让用户每 15 分钟左右刷新一次页面,它是一个单页应用程序。

所以,我尝试将slidingExpiration 更改为“true”并每 17 分钟创建一个 ping(使用 ajax),它确实停止了会话过期和烦人的消息 - 但 cookie 在 3 小时后没有过期(我用记住我'false'登录)!

我能做什么?

【问题讨论】:

    标签: c# asp.net-mvc-4 session cookies single-page-application


    【解决方案1】:

    在 IIS 管理控制台中右键单击您的应用程序池并查找“空闲超时(分钟)”。

    您可以将设置调整为 0(零),这会有效地禁用超时,这样应用程序池就不会因为空闲而关闭。

    【讨论】:

    • 好的,它会解决 20 分钟到期的问题,但我可以在记住我真实(将保持“永远”)和记住我错误(停留 3 小时)之间做出不同吗只)?
    • 你可以把逻辑放在cookies中,然后在javascript中处理。
    • 由于我使用“[Authorize(Roles = "Role1")]" 属性 - 我想使用原始 cookie 而不是处理另一个
    【解决方案2】:

    我会仔细检查您的 IIS 设置。查看您的应用程序池空闲超时值设置为多少。默认为 20 分钟。当应用程序池空闲(没有用户访问应用程序池)二十分钟后,您将失去会话状态(会话中存储的所有数据都将被清除)。

    如果用户越多,这个问题就会自行解决,但假设您是唯一测试的人,将此值增加到大于 180 分钟将防止超时,或者您可以将该值设置为零以禁用应用程序池空闲完全超时。

    有关在 IIS Express 中检查应用程序池超时的信息,请参阅此答案...https://stackoverflow.com/a/10222419/386856

    请注意,死掉的应用程序池可能需要几秒钟才能重新生成。无论如何,增加这个值可能是有益的。如果用户碰巧是不幸不得不等待应用程序池重新启动的人,这将防止用户体验极其缓慢。

    更新

    要允许未单击记住我的用户更改超时,您可以创建自定义属性,也可以通过 C# 修改 FormsAuthentication 超时。这是通过代码设置超时的很好的参考。 https://msdn.microsoft.com/en-us/library/system.web.configuration.formsauthenticationconfiguration.timeout%28v=vs.110%29.aspxhttps://msdn.microsoft.com/en-us/library/system.web.configuration.formsauthenticationconfiguration(v=vs.110).aspx 如果您希望超时在 3 小时后到期,即使用户有活动,请确保在您的 Web 配置中 slipExpiration 为 false。如果您希望超时在用户最后一次活动后 3 小时到期,请确保 slipExpiration 为 true。因此,在设置 cookie 之前,请尝试以下操作(请务必检查 OpenWebConfiguration 路径):

        System.Configuration.Configuration configuration = WebConfigurationManager.OpenWebConfiguration("/aspnetTest");
        AuthenticationSection authenticationSection = (AuthenticationSection)configuration.GetSection("system.web/authentication");
        FormsAuthenticationConfiguration formsAuthentication = authenticationSection.Forms;
        formsAuthentication.Timeout = System.TimeSpan.FromHours(3);
        formsAuthentication.SlidingExpiration = true;
    

    【讨论】:

    • 好的,它可以解决 20 分钟到期的问题,而无需 ping,但我可以在记住我真实(将保持“永远”)和记住我错误之间做出不同(仅停留 3 小时)?
    • 你可能需要一个代码实现。我会说提高您的应用程序池空闲超时,因为这可能是个好主意。然后使用操作过滤器属性检查是否记住我假用户已登录超过 3 小时。如果是这样就踢他们,如果不是,让他们继续。进行检查的一种方法是将登录时间存储在 cookie 中,然后在属性中验证没有超过三个小时。
    • 添加了另一个替代答案,见上文。
    • 有没有办法让“rememberMe = true”的SlidingExpiration为真,“rememberMe = false”为假?它会解决我所有的问题...
    • 我会看看@TamarG
    【解决方案3】:

    我通过创建我自己的加密 cookie 解决了这个问题,如果存在的话,它将保持用户的会话。在操作过滤器属性中,检查用户的会话是否已过期,检查此 cookie。如果 cookie 存在,请验证信息并重新建立会话。如果用户没有会话、没有 cookie 或加密凭据不正确,则将用户重定向到登录页面。由于一切都在代码中处理,因此无需对服务器设置进行猜测。

    登录时勾选记住我:

                if (RememberMe ?? false)
                {
                    var authCookie = new HttpCookie(Config.AuthorizationCookie);
    
                    authCookie.Values.Add("ID", Crypto.EncryptStringAES(UserSession.Current.Account.ID.ToString(), Config.SharedSecret));
    
                    authCookie.Expires = DateTime.Now.AddDays(30);
                    AuthorizationCookie = authCookie;
                }
                Response.AppendCookie(AuthorizationCookie);
    

    在没有会话的情况下访问页面(在附加到必要控制器和操作的属性内完成):

            _account = UserSession.Current.Account;
    
            // check if there is currently an account in the session
            if(_account == null)
            {
                // check the user authorization cookie for a user id
                HttpCookie authCookie = HttpContext.Current.Request.Cookies[Config.AuthorizationCookie] ?? new HttpCookie(Config.AuthorizationCookie);
                if (authCookie.HasKeys && !String.IsNullOrEmpty(authCookie["ID"]))
                {
                    // decrypt the user id for database lookup
                    _userID = Crypto.DecryptStringAES(authCookie.Values["ID"], Config.SharedSecret);
                }
            }
    

    必要时重新建立会话(在数据库连接内完成)

        // called within a database connection's using statement
        protected override void Query(DatabaseContext db)
        {
            // check the database for the user account
            if(_account == null && !String.IsNullOrEmpty(_userID))
            {
                int tempID;
                int? id;
                id = int.TryParse(_userID, out tempID) ? tempID : (int?)null;
    
                if(id.HasValue)
                {
                    _sessionRestored = true;
                    _account = db.User.Find(id);
    
                    if(_account != null)
                    {
                        _account.LastLogon = DateTime.UtcNow;
                        db.SaveChanges();
                    }
                }
            }
        }
    

    最终恢复会话:

            if (_account != null)
            {
                // set the current account
                UserSession.Current.Account = _account;
    
                if(_sessionRestored)
                {
                    UserSession.Current.Refresh();
                }
            }
    

    您的代码可能看起来有些不同,但这就是我如何做到这一点的关键。

    【讨论】:

    • 我能看到的唯一问题是,如果您在会话中存储了一些数据,它将在 20 分钟后被清除。听起来不像是 ops 的情况,但确实想考虑这种可能性。
    • @NathanRenico 好点子,我现在并不想在这个项目的会话中坚持任何东西。将该信息存储在数据库中或使用 cookie 似乎是一个不错的方法。我认为这可能是 OP 想要的 technet.microsoft.com/en-us/library/cc725820%28v=ws.10%29.aspx
    • 您在何时何地“最终恢复会话”?
    • 我不明白具体要做什么,例如Visual Studio找不到“Config”和“UserSession”
    • Config 和 UserSession 只是我为运行我的项目而创建的类。听起来你想要一些开箱即用的东西,我建议更多的是自定义解决方案。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-10-18
    • 2012-08-23
    • 2013-06-13
    • 1970-01-01
    • 2014-07-20
    • 1970-01-01
    • 2016-11-08
    相关资源
    最近更新 更多