Generating a new ASP.NET session in the current HTTPContext
I would like to share my magic. Actually, no, its not yet magical.. We ought to test and evolve the code more.I only tested these code in with-cookie, InProc session mode. Put these method inside your page, and call it where you need the ID to be regenerated (please set your web app to Full Trust):
void regenerateId(){ System.Web.SessionState.SessionIDManager manager = new System.Web.SessionState.SessionIDManager(); string oldId = manager.GetSessionID(Context); string newId = manager.CreateSessionID(Context); bool isAdd = false, isRedir = false; manager.SaveSessionID(Context, newId, out isRedir, out isAdd); HttpApplication ctx = (HttpApplication)HttpContext.Current.ApplicationInstance; HttpModuleCollection mods = ctx.Modules; System.Web.SessionState.SessionStateModule ssm = (SessionStateModule)mods.Get("Session"); System.Reflection.FieldInfo[] fields = ssm.GetType().GetFields(BindingFlags.NonPublic | BindingFlags.Instance); SessionStateStoreProviderBase store = null; System.Reflection.FieldInfo rqIdField = null, rqLockIdField = null, rqStateNotFoundField = null; foreach (System.Reflection.FieldInfo field in fields) { if (field.Name.Equals("_store")) store = (SessionStateStoreProviderBase)field.GetValue(ssm); if (field.Name.Equals("_rqId")) rqIdField = field; if (field.Name.Equals("_rqLockId")) rqLockIdField = field; if (field.Name.Equals("_rqSessionStateNotFound")) rqStateNotFoundField = field; } object lockId = rqLockIdField.GetValue(ssm); if ((lockId != null) && (oldId !=null)) store.ReleaseItemExclusive(Context, oldId, lockId); rqStateNotFoundField.SetValue(ssm, true); rqIdField.SetValue(ssm, newId);}
I have been digging around .NET Source code (that were available in http://referencesource.microsoft.com/netframework.aspx), and discovered that there is no way I could regenerate SessionID without hacking the internals of session management mechanism. So I do just that - hack SessionStateModule internal fields, so it will save the current Session into a new ID. Maybe the current HttpSessionState object still has the previous Id, but AFAIK the SessionStateModule ignored it. It just use the internal _rqId field when it has to save the state somewhere. I have tried other means, like copying SessionStateModule into a new class with a regenerate ID functionality, (I was planning to replace SessionStateModule with this class), but failed because it currently has references to other internal classes (like InProcSessionStateStore). The downside of hacking using reflection is we need to set our application to 'Full Trust'.
Oh, and if you really need the VB version, try these :
Sub RegenerateID() Dim manager Dim oldId As String Dim newId As String Dim isRedir As Boolean Dim isAdd As Boolean Dim ctx As HttpApplication Dim mods As HttpModuleCollection Dim ssm As System.Web.SessionState.SessionStateModule Dim fields() As System.Reflection.FieldInfo Dim rqIdField As System.Reflection.FieldInfo Dim rqLockIdField As System.Reflection.FieldInfo Dim rqStateNotFoundField As System.Reflection.FieldInfo Dim store As SessionStateStoreProviderBase Dim field As System.Reflection.FieldInfo Dim lockId manager = New System.Web.SessionState.SessionIDManager oldId = manager.GetSessionID(Context) newId = manager.CreateSessionID(Context) manager.SaveSessionID(Context, newId, isRedir, isAdd) ctx = HttpContext.Current.ApplicationInstance mods = ctx.Modules ssm = CType(mods.Get("Session"), System.Web.SessionState.SessionStateModule) fields = ssm.GetType.GetFields(System.Reflection.BindingFlags.NonPublic Or System.Reflection.BindingFlags.Instance) store = Nothing : rqLockIdField = Nothing : rqIdField = Nothing : rqStateNotFoundField = Nothing For Each field In fields If (field.Name.Equals("_store")) Then store = CType(field.GetValue(ssm), SessionStateStoreProviderBase) If (field.Name.Equals("_rqId")) Then rqIdField = field If (field.Name.Equals("_rqLockId")) Then rqLockIdField = field If (field.Name.Equals("_rqSessionStateNotFound")) Then rqStateNotFoundField = field Next lockId = rqLockIdField.GetValue(ssm) If ((Not IsNothing(lockId)) And (Not IsNothing(oldId))) Then store.ReleaseItemExclusive(Context, oldId, lockId) rqStateNotFoundField.SetValue(ssm, True) rqIdField.SetValue(ssm, newId)End Sub
If you're security concious and would like the C# version of this answer removing the old field, please use the following.
private static void RegenerateSessionId(){ // Initialise variables for regenerating the session id HttpContext Context = HttpContext.Current; SessionIDManager manager = new SessionIDManager(); string oldId = manager.GetSessionID(Context); string newId = manager.CreateSessionID(Context); bool isAdd = false, isRedir = false; // Save a new session ID manager.SaveSessionID(Context, newId, out isRedir, out isAdd); // Get the fields using the below and create variables for storage HttpApplication ctx = HttpContext.Current.ApplicationInstance; HttpModuleCollection mods = ctx.Modules; SessionStateModule ssm = (SessionStateModule)mods.Get("Session"); FieldInfo[] fields = ssm.GetType().GetFields(BindingFlags.NonPublic | BindingFlags.Instance); SessionStateStoreProviderBase store = null; FieldInfo rqIdField = null, rqLockIdField = null, rqStateNotFoundField = null; SessionStateStoreData rqItem = null; // Assign to each variable the appropriate field values foreach (FieldInfo field in fields) { if (field.Name.Equals("_store")) store = (SessionStateStoreProviderBase)field.GetValue(ssm); if (field.Name.Equals("_rqId")) rqIdField = field; if (field.Name.Equals("_rqLockId")) rqLockIdField = field; if (field.Name.Equals("_rqSessionStateNotFound")) rqStateNotFoundField = field; if (field.Name.Equals("_rqItem")) rqItem = (SessionStateStoreData)field.GetValue(ssm); } // Remove the previous session value object lockId = rqLockIdField.GetValue(ssm); if ((lockId != null) && (oldId != null)) store.RemoveItem(Context, oldId, lockId, rqItem); rqStateNotFoundField.SetValue(ssm, true); rqIdField.SetValue(ssm, newId);}
As Can Gencer mentioned - ReleaseItemExclusive doesn't remove old session from the store and that leads to that session eventually expiring and calling Session_End in Global.asax. This caused us a huge problem in production, because we are clearing Thread identity in Session_End, and because of this - users were spontaneously losing authentication on thread.
So below is the corrected code that works.
Dim oHTTPContext As HttpContext = HttpContext.CurrentDim oSessionIdManager As New SessionIDManagerDim sOldId As String = oSessionIdManager.GetSessionID(oHTTPContext)Dim sNewId As String = oSessionIdManager.CreateSessionID(oHTTPContext)Dim bIsRedir As Boolean = FalseDim bIsAdd As Boolean = FalseoSessionIdManager.SaveSessionID(oHTTPContext, sNewId, bIsRedir, bIsAdd)Dim oAppContext As HttpApplication = HttpContext.Current.ApplicationInstanceDim oModules As HttpModuleCollection = oAppContext.ModulesDim oSessionStateModule As SessionStateModule = _ DirectCast(oModules.Get("Session"), SessionStateModule)Dim oFields() As FieldInfo = _ oSessionStateModule.GetType.GetFields(BindingFlags.NonPublic Or _ BindingFlags.Instance)Dim oStore As SessionStateStoreProviderBase = NothingDim oRqIdField As FieldInfo = NothingDim oRqItem As SessionStateStoreData = NothingDim oRqLockIdField As FieldInfo = NothingDim oRqStateNotFoundField As FieldInfo = NothingFor Each oField As FieldInfo In oFields If (oField.Name.Equals("_store")) Then oStore = DirectCast(oField.GetValue(oSessionStateModule), _ SessionStateStoreProviderBase) End If If (oField.Name.Equals("_rqId")) Then oRqIdField = oField End If If (oField.Name.Equals("_rqLockId")) Then oRqLockIdField = oField End If If (oField.Name.Equals("_rqSessionStateNotFound")) Then oRqStateNotFoundField = oField End If If (oField.Name.Equals("_rqItem")) Then oRqItem = DirectCast(oField.GetValue(oSessionStateModule), _ SessionStateStoreData) End IfNextIf oStore IsNot Nothing Then Dim oLockId As Object = Nothing If oRqLockIdField IsNot Nothing Then oLockId = oRqLockIdField.GetValue(oSessionStateModule) End If If (oLockId IsNot Nothing) And (Not String.IsNullOrEmpty(sOldId)) Then oStore.ReleaseItemExclusive(oHTTPContext, sOldId, oLockId) oStore.RemoveItem(oHTTPContext, sOldId, oLockId, oRqItem) End If oRqStateNotFoundField.SetValue(oSessionStateModule, True) oRqIdField.SetValue(oSessionStateModule, sNewId)End If