在当前的HTTPContext中生成一个新的ASP.NET会话
由于对我们的一些产品进行渗透testing,当时看起来是一个“简单”的问题需要解决的事情变得艰难。
当然不是,当然,我的意思是为什么要为当前的HTTPContext
生成全新的会话非常困难? 离奇! 无论如何,我已经写了一个厚脸皮的小实用程序课,“只是做”:
(道歉代码格式化/突出显示/ Visual Basic我必须做错了什么)
Imports System.Web Imports System.Web.SessionState Public Class SwitchSession Public Shared Sub SetNewSession(ByVal context As HttpContext) ' This value will hold the ID managers action to creating a response cookie Dim cookieAdded As Boolean ' We use the current session state as a template Dim state As HttpSessionState = context.Session ' We use the default ID manager to generate a new session id Dim idManager As New SessionIDManager() ' We also start with a new, fresh blank state item collection Dim items As New SessionStateItemCollection() ' Static objects are extracted from the current session context Dim staticObjects As HttpStaticObjectsCollection = _ SessionStateUtility.GetSessionStaticObjects(context) ' We construct the replacement session for the current, some parameters are new, others are taken from previous session Dim replacement As New HttpSessionStateContainer( _ idManager.CreateSessionID(context), _ items, _ staticObjects, _ state.Timeout, _ True, _ state.CookieMode, _ state.Mode, _ state.IsReadOnly) ' Finally we strip the current session state from the current context SessionStateUtility.RemoveHttpSessionStateFromContext(context) ' Then we replace the assign the active session state using the replacement we just constructed SessionStateUtility.AddHttpSessionStateToContext(context, replacement) ' Make sure we clean out the responses of any other inteferring cookies idManager.RemoveSessionID(context) ' Save our new cookie session identifier to the response idManager.SaveSessionID(context, replacement.SessionID, False, cookieAdded) End Sub End Class
它对请求的其余部分工作正常,并正确地将自己标识为新的会话(例如HTTPContext.Current.Session.SessionID
返回新生成的会话标识符)。
让人吃惊的是,当下一个请求到达服务器时, HTTPContext.Session
(一个HTTPSessionState
对象)使用正确的SessionID
标识自己,但IsNewSession
设置为True
,并且是空的,失去了前一个请求中设置的所有会话值。
所以对于前面的HTTPSessionState
对象,从这个初始请求中删除,这里是一个事件处理程序,还有一个callback,处理持续请求中的会话数据的东西,还是只是我缺less的东西,必须有一些特殊的东西?
任何人都有什么魔力可以分享?
我想分享我的魔力。 实际上,不,它不是神奇的..我们应该更多地testing和演化代码。 我只用-cooker,InProc会话模式testing了这些代码。 把这些方法放在你的页面中,然后在需要重新生成ID的地方调用它(请将你的Web应用设置为完全信任):
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); }
我一直在研究.NET源代码( http://referencesource.microsoft.com/netframework.aspx ),并发现无法重新创buildSessionID而不会破坏会话pipe理机制的内部。 所以我只是这样做 – 破坏SessionStateModule内部字段,所以它会将当前Session保存到一个新的ID中。 也许当前的HttpSessionState对象仍然有以前的Id,但AFAIK SessionStateModule忽略它。 它只是在内部_rqId字段的地方保存状态。 我尝试过其他方法,比如将SessionStateModule复制到一个具有重新生成IDfunction的新类中(我计划用这个类来replaceSessionStateModule),但是因为当前引用了其他内部类(如InProcSessionStateStore)而失败。 黑客使用反思的缺点是我们需要将我们的应用程序设置为“完全信任”。
哦,如果你真的需要VB版本,试试这些:
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
正如Genger所说 – ReleaseItemExclusive不会从存储中删除旧会话,并导致该会话最终过期并在Global.asax中调用Session_End。 这给我们带来了一个巨大的生产问题,因为我们正在清除Session_End中的Thread标识,正因为如此 – 用户在线程上自发地失去了身份validation。
所以下面是修正后的代码。
Dim oHTTPContext As HttpContext = HttpContext.Current Dim oSessionIdManager As New SessionIDManager Dim sOldId As String = oSessionIdManager.GetSessionID(oHTTPContext) Dim sNewId As String = oSessionIdManager.CreateSessionID(oHTTPContext) Dim bIsRedir As Boolean = False Dim bIsAdd As Boolean = False oSessionIdManager.SaveSessionID(oHTTPContext, sNewId, bIsRedir, bIsAdd) Dim oAppContext As HttpApplication = HttpContext.Current.ApplicationInstance Dim oModules As HttpModuleCollection = oAppContext.Modules Dim oSessionStateModule As SessionStateModule = _ DirectCast(oModules.Get("Session"), SessionStateModule) Dim oFields() As FieldInfo = _ oSessionStateModule.GetType.GetFields(BindingFlags.NonPublic Or _ BindingFlags.Instance) Dim oStore As SessionStateStoreProviderBase = Nothing Dim oRqIdField As FieldInfo = Nothing Dim oRqItem As SessionStateStoreData = Nothing Dim oRqLockIdField As FieldInfo = Nothing Dim oRqStateNotFoundField As FieldInfo = Nothing For 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 If Next If 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
你有没有考虑使用HttpSessionState.Abandon方法 ? 这应该清除一切。 然后开始一个新的会话,并使用上面代码中存储的所有项目进行填充。
Session.Abandon();
应该足够了。 否则,如果它仍然固执的话,你可以试着多打几个电话:
Session.Contents.Abandon(); Session.Contents.RemoveAll();
你不能只是设置:
<sessionState regenerateExpiredSessionId="False" />
在web.config中,然后使用Ahmadbuild议的解决scheme?
对于MVC4请有这样的代码:
System.Web.SessionState.SessionIDManager manager = new System.Web.SessionState.SessionIDManager(); HttpContext Context = System.Web.HttpContext.Current; 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)System.Web.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);