Session Manager will not log me out when session e

2019-07-17 02:49发布

问题:

I'm having this issue with my current session manager, when the session expires it will not log off the user.

At random intervals while user is logged in the HttpContext.Current is NULL causing the site to generate many errors. I've tried a couple of techniques to redirect user to login upon session expiration, with no success.

  1. Under Global.asax I'm making use of Session_End can attempted to call LogOut method but this gets executed even before user logs in.
  2. Under Global.asax I added Application_AcquireRequestState, but sadly this makes way to many calls to the SSO Service. Also, it never redirected to login upon session expiration. I tested it with the following FormsAuthentication.RedirectToLoginPage(); No luck.

I found this answer by Lex Li "Why HttpContext.Current be null?" - it gave me some insight on what my problem could be, even-though my application doesn't make use of background-threads, my HttpContext.Current comes back Null with some random requests, doesn't always happen.

Session Manager

   public class SessionManager
    {
        private const string SessionKey = "AppSession";

        private SessionManager()
        {
            GUID                  = new Guid();
            FirstName             = String.Empty;
            LastName              = String.Empty;
            Email                 = String.Empty;
            SessionExpiration     = new DateTime();
        }

        // Gets the current session.
        public static SessionManager Current
        {
            get
            {
                if(HttpContext.Current != null)
                {
                    if (HttpContext.Current.Session[SessionKey] == null)
                    {
                        var model = new SessionManager();
                        HttpContext.Current.Session[SessionKey] = model;
                    }
                    return (SessionManager)HttpContext.Current.Session[SessionKey];
                }
                else
                {
                    return null;
                }
            }

        }

        public static void LogOffUser()
        {
            //SSO is a seperate service, I need to stay insync.
            var ssoAuth = new SSOAuth(); 
            if(Current != null)
               ssoAuth.SSOLogoffUser(Current.GUID);

            FormsAuthentication.SignOut();  
            FormsAuthentication.RedirectToLoginPage();
        }

        public Guid GUID { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public string Email { get; set; }
        public DateTime SessionExpiration { get; set; }
    }

Login Controller

[HttpPost]
[AllowAnonymous]
public JsonResult Login(LoginViewModel user)
{
    var ssoAuth = new SSOAuth();
    var sessionObject = ssoAuth.SSOLoginUser(user.Email, user.Password);

    if (sessionObject != null && sessionObject.SessionId != Guid.Empty)
    {
        SessionManager.Current.Email                 = sessionObject.UserId;
        SessionManager.Current.GUID                  = sessionObject.SessionId;
        SessionManager.Current.FirstName             = sessionObject.FirstName;
        SessionManager.Current.LastName              = sessionObject.LastName;
        SessionManager.Current.SessionExpiration     = sessionObject.SessionExpiration;

        //Grab difference in time to get session timeout in minutes.
        var differenceInTime = SessionManager.Current.SessionExpiration - DateTime.Now;
        Session.Timeout = differenceInTime.Minutes;

        //Authenticate user
        FormsAuthentication.SetAuthCookie(user.Email, false);

        //We return JSON, Its an AJAX Call.
        return Json(new
        {
            redirectUrl = "/Home.mvc/Home/Index",
            isRedirect = true
        });
    }

    return Json(new
    {
        redirectUrl = string.Empty,
        isRedirect = false
    });
}

回答1:

This sounds suspiciously like it may be a session vs. forms authentication timing issue similar to Forms Authentication Timeout vs Session Timeout

Make sure your Forms timeout is at least 2x your session timeout.



回答2:

As confirmed in the comments, you're setting your application pool's Maximum number of worker processes to a value higher than 1 and use In-Process Mode for session state.

I'm pretty sure this is the problem of HttpContext.Current.Session being null randomly instead of HttpContext.Current.

InProc session state and multiple working processes are not compatible. With InProc session state, your session state is stored in worker processes memory and is not shared between worker processes => that results in session state being lost randomly when your requests are served by different processes.

In your case, setting Maximum number of worker processes to 1 should fix the issue.

If you're looking for a multiple working processes solution, you should store your session state out of process using either a session state service or a database: http://tutorials.csharp-online.net/ASP.NET_State_Management%E2%80%94Storing_Session_State_out_of_Process



回答3:

I think that you may need to verify that you have this set in your web.config

<httpRuntime targetFramework="4.5" />

It seems that you may not have the task friendly awaits and that will cause unexpected behaviors such as this. You may refer to the similar problem posed here and the solution.

Now, you may not be using Asynchronous code in your soution, but that doesn't mean that it isn't being used by the framework. Regardless, I think it is worth a shot.