Cookies and Sessions Expiration in .NET

萝らか妹 提交于 2019-12-24 03:55:12

问题


I have an MVC4 single application page. In the log-in page there are 3 fields: user, password and "remember me" checkbox. The C# login code is this:

   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.");
   }

I want to do this:

  • If a user logs in with "remember me" true - The cookie will stay until the user logs off.

  • If the user logs in with "remember me" false - The cookie will expire after 3 hours.

In web.config I have this code:

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

The problem is when the user doesn't touch the page for a short time (10-30 mins usually), and then the user tries to do something in the page - there is an error

"Authorization has been denied for this request."

(Although sessionStation is more than 30 minutes!)

After the user refresh the page - if the cookie hasn't expired yet - everything works fine. But of course I don't want to make the user refresh the page every 15 minutes or so, it's a single-page-application.

So, I tried to change slidingExpiration to "true" and create a ping (with ajax) every 17 minutes and it really stopped the session expiration and the annoying message - but the cookie didn't expired after 3 hours (I logged in with remember me 'false')!

What can I do?


回答1:


Right click your application pool from IIS management console and look for "Idle time-out(minutes)".

You can adjust the setting to 0 (zero) which effectively disables the timeout so that the application pool will never shut down due to being idle.




回答2:


I would double check your IIS settings. See what your App Pool Idle Timeout value is set to. By default it's 20 minutes. When the App Pool goes idle (no users accessing the app pool) for twenty minutes, you will loose session state (All data stored in the session will be cleared).

With more users this problem would work itself out, but presuming you are the only person testing, increasing this value to something greater than 180 minutes will prevent the timeout, or you could set the value to zero to disable the app pool idle timeout altogether.

See this answer for information on checking your app pool timeout in IIS Express... https://stackoverflow.com/a/10222419/386856

Do note that a dead app pool can take several seconds to re-spawn. It may be beneficial to increase this value anyways. This will prevent users from having an extremely slow experience if they happen to be the person that's unlucky enough to have to wait for the app pool to restart.

Update

To allow for a change in timeout for users who don't click remember me, you can create a custom attribute or you could modify the FormsAuthentication timeout via C#. Here are good references on setting the timeout via code. https://msdn.microsoft.com/en-us/library/system.web.configuration.formsauthenticationconfiguration.timeout%28v=vs.110%29.aspx and https://msdn.microsoft.com/en-us/library/system.web.configuration.formsauthenticationconfiguration(v=vs.110).aspx If you want the timeout to expire right at 3 hours, even if the user has activity be sure slidingExpiration is false in your web config. If you want the timeout to expire 3 hours after the user's last activity be sure slidingExpiration is true. So before you set the cookie try something like the following (Be sure to check the OpenWebConfiguration path):

    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;



回答3:


I solved this by just creating my own encrypted cookie which will persist the user's session if present. In an action filter attribute, check if the user's session is expired, check for this cookie. If the cookie exists, verify the information and reestablish the session. If the user doesn't have a session, doesn't have a cookie, or the encrypted credentials are incorrect, redirect the user to the login page. Since everything is handled in the code, there is no guess work on the server settings.

On login with remember me checked:

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

On page visit without session (done inside an attribute attached to necessary controllers and actions):

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

Re-establish the session if necessary (done inside a database connection)

    // 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();
                }
            }
        }
    }

Finally restore the session:

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

            if(_sessionRestored)
            {
                UserSession.Current.Refresh();
            }
        }

Your code may look a bit different but that is the crux of how I did it.



来源:https://stackoverflow.com/questions/28134459/cookies-and-sessions-expiration-in-net

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!