ASP.NET:为无 cookie 会话实现 ISessionIDManager?
Posted
技术标签:
【中文标题】ASP.NET:为无 cookie 会话实现 ISessionIDManager?【英文标题】:ASP.NET: Implementing ISessionIDManager for cookieless sessions? 【发布时间】:2011-11-29 05:20:18 【问题描述】:问题:
我正在编写一个自定义会话提供程序。 到目前为止,它运行良好。 我决定添加一个自定义的 ISessionIDManager 来控制会话 ID。
它已经适用于 cookie 会话。 但是当我切换到 cookieless 时,就像这样:
<sessionState mode="Custom" customProvider="custom_provider" cookieless="true" timeout="1"
sessionIDManagerType="Samples.AspNet.Session.MySessionIDManager"
sqlConnectionString="Data Source=localhost;Initial Catalog=TestDB;User Id=SomeUser;Password=SomePassword;"
sqlCommandTimeout="10"
>
<!-- timeout in minutes-->
<providers>
<add name="custom_provider" type="Test.WebSession.CustomSessionStoreProvider" />
</providers>
</sessionState>
然后它重定向到:http://localhost:52897/(77bb065f-d2e9-4cfc-8117-8b89a40e00d8)/default.aspx 这会引发 HTTP 404。 我明白为什么,因为没有这样的文件夹。 但是,当您使用默认会话管理器(asp.net 附带的)并切换到无 cookie 时,URL 看起来像这样:http://localhost:52897/(S(sq2abm453wnasg45pvboee45))/DisplaySessionValues.aspx 并且没有 HTTP 404... 我尝试将 (S and ) 添加到我的会话 ID 中,放在 url 的括号中,但这没有帮助。
我错过了什么?
using System;
using System.Configuration;
using System.Web.Configuration;
using System.Web;
using System.Web.SessionState;
// http://allantech.blogspot.com/2011/04/cookieless-session-state-in-aspnet.html
// http://forums.asp.net/t/1082784.aspx/1
// http://***.com/questions/4612310/implementing-a-custom-sessionidmanager
// http://msdn.microsoft.com/en-us/library/system.web.sessionstate.isessionidmanager.aspx
// http://msdn.microsoft.com/en-us/library/system.web.sessionstate.isessionidmanager(v=vs.80).aspx
namespace Samples.AspNet.Session
// Samples.AspNet.Session.MySessionIDManager
public class MySessionIDManager : IHttpModule, ISessionIDManager
protected SessionStateSection pConfig = null;
internal const string HeaderName = "AspFilterSessionId";
protected void InitializeModule()
// Obtain session-state configuration settings.
if (pConfig == null)
Configuration cfg =
WebConfigurationManager.OpenWebConfiguration(System.Web.Hosting.HostingEnvironment.ApplicationVirtualPath);
pConfig = (SessionStateSection)cfg.GetSection("system.web/sessionState");
// End if (pConfig == null)
//
// IHttpModule Members
//
//
// IHttpModule.Init
//
public void Init(HttpApplication app)
//InitializeModule();
// End Sub Init
//
// IHttpModule.Dispose
//
public void Dispose()
// End Sub Dispose
//
// ISessionIDManager Members
//
//
// ISessionIDManager.Initialize
//
public void Initialize()
InitializeModule();
// End Sub Initialize
//
// ISessionIDManager.InitializeRequest
//
public bool InitializeRequest(
HttpContext context,
bool suppressAutoDetectRedirect,
out bool supportSessionIDReissue
)
if (pConfig.Cookieless == HttpCookieMode.UseCookies)
supportSessionIDReissue = false;
return false;
else
supportSessionIDReissue = true;
return context.Response.IsRequestBeingRedirected;
// End Function InitializeRequest
//
// ISessionIDManager.GetSessionID
//
public string GetSessionID(HttpContext context)
string id = null;
if (pConfig.Cookieless == HttpCookieMode.UseUri)
string tmp = context.Request.Headers[HeaderName];
if (tmp != null)
id = HttpUtility.UrlDecode(id);
// Retrieve the SessionID from the URI.
else
if (context.Request.Cookies.Count > 0)
id = context.Request.Cookies[pConfig.CookieName].Value;
id = HttpUtility.UrlDecode(id);
// Verify that the retrieved SessionID is valid. If not, return null.
if (!Validate(id))
id = null;
return id;
// End Function GetSessionID
//
// ISessionIDManager.CreateSessionID
//
public string CreateSessionID(HttpContext context)
return System.Guid.NewGuid().ToString();
// End Function CreateSessionID
//
// ISessionIDManager.RemoveSessionID
//
public void RemoveSessionID(HttpContext context)
context.Response.Cookies.Remove(pConfig.CookieName);
// End Sub RemoveSessionID
public static string InsertSessionId(string id, string path)
string dir = GetDirectory(path);
if (!dir.EndsWith("/"))
dir += "/";
string appvpath = HttpRuntime.AppDomainAppVirtualPath;
if (!appvpath.EndsWith("/"))
appvpath += "/";
if (path.StartsWith(appvpath))
path = path.Substring(appvpath.Length);
if (path[0] == '/')
path = path.Length > 1 ? path.Substring(1) : "";
// //http://localhost:52897/(S(sq2abm453wnasg45pvboee45))/DisplaySessionValues.aspx
return Canonic(appvpath + "(" + id + ")/" + path);
//return Canonic(appvpath + "(S(" + id + "))/" + path);
public static bool IsRooted(string path)
if (path == null || path.Length == 0)
return true;
char c = path[0];
if (c == '/' || c == '\\')
return true;
return false;
public static string Canonic(string path)
char[] path_sep = '\\', '/' ;
bool isRooted = IsRooted(path);
bool endsWithSlash = path.EndsWith("/");
string[] parts = path.Split(path_sep);
int end = parts.Length;
int dest = 0;
for (int i = 0; i < end; i++)
string current = parts[i];
if (current.Length == 0)
continue;
if (current == ".")
continue;
if (current == "..")
dest--;
continue;
if (dest < 0)
if (!isRooted)
throw new HttpException("Invalid path.");
else
dest = 0;
parts[dest++] = current;
if (dest < 0)
throw new HttpException("Invalid path.");
if (dest == 0)
return "/";
string str = String.Join("/", parts, 0, dest);
str = RemoveDoubleSlashes(str);
if (isRooted)
str = "/" + str;
if (endsWithSlash)
str = str + "/";
return str;
public static string GetDirectory(string url)
url = url.Replace('\\', '/');
int last = url.LastIndexOf('/');
if (last > 0)
if (last < url.Length)
last++;
return RemoveDoubleSlashes(url.Substring(0, last));
return "/";
public static string RemoveDoubleSlashes (string input)
// MS VirtualPathUtility removes duplicate '/'
int index = -1;
for (int i = 1; i < input.Length; i++)
if (input [i] == '/' && input [i - 1] == '/')
index = i - 1;
break;
if (index == -1) // common case optimization
return input;
System.Text.StringBuilder sb = new System.Text.StringBuilder(input.Length);
sb.Append (input, 0, index);
for (int i = index; i < input.Length; i++)
if (input [i] == '/')
int next = i + 1;
if (next < input.Length && input [next] == '/')
continue;
sb.Append ('/');
else
sb.Append (input [i]);
return sb.ToString ();
// http://www.dotnetfunda.com/articles/article1531-how-to-add-custom-headers-into-readonly-httprequest-object-using-httpmodule-.aspx
public void SetHeader(string strHeaderName, string strValue)
//get a reference
System.Collections.Specialized.NameValueCollection headers = HttpContext.Current.Request.Headers;
//get a type
Type t = headers.GetType();
//get the property
System.Reflection.PropertyInfo prop = t.GetProperty(
"IsReadOnly",
System.Reflection.BindingFlags.Instance
| System.Reflection.BindingFlags.IgnoreCase
| System.Reflection.BindingFlags.NonPublic
| System.Reflection.BindingFlags.FlattenHierarchy
| System.Reflection.BindingFlags.NonPublic
| System.Reflection.BindingFlags.Public
| System.Reflection.BindingFlags.FlattenHierarchy
);
//unset readonly
prop.SetValue(headers, false, null); // Set Read-Only to false
//add a header
//HttpContext.Current.Request.Headers.Add(strHeaderName, strValue);
//headers.Add(strHeaderName, strValue);
t.InvokeMember("BaseAdd",
System.Reflection.BindingFlags.InvokeMethod
| System.Reflection.BindingFlags.NonPublic
| System.Reflection.BindingFlags.Instance,
null,
headers,
new object[] strHeaderName, new System.Collections.ArrayList strValue
);
prop.SetValue(headers, true, null); // Reset Read-Only to true
// Victory !
//string strCheckHeaders = string.Join(Environment.NewLine, HttpContext.Current.Request.Headers.AllKeys);
//
// ISessionIDManager.SaveSessionID
//
public void SaveSessionID(HttpContext context, string id, out bool redirected, out bool cookieAdded)
if (!Validate(id))
throw new HttpException("Invalid session ID");
Type t = base.GetType();
redirected = false;
cookieAdded = false;
if (pConfig.Cookieless == HttpCookieMode.UseUri)
// Add the SessionID to the URI. Set the redirected variable as appropriate.
//context.Request.Headers.Add(HeaderName, id);
//context.Request.Headers.Set(HeaderName, id);
SetHeader(HeaderName, id);
cookieAdded = false;
redirected = true;
UriBuilder newUri = new UriBuilder(context.Request.Url);
newUri.Path = InsertSessionId(id, context.Request.FilePath);
//http://localhost:52897/(S(sq2abm453wnasg45pvboee45))/DisplaySessionValues.aspx
context.Response.Redirect(newUri.Uri.PathAndQuery, false);
context.ApplicationInstance.CompleteRequest(); // Important !
return;
else
context.Response.Cookies.Add(new HttpCookie(pConfig.CookieName, id));
cookieAdded = true;
// End Sub SaveSessionID
//
// ISessionIDManager.Validate
//
public bool Validate(string id)
try
Guid testGuid = new Guid(id);
if (id == testGuid.ToString())
return true;
catch
return false;
// End Function Validate
// End Class MySessionIDManager : IHttpModule, ISessionIDManager
// End Namespace Samples.AspNet.Session
【问题讨论】:
我正在尝试做一些和你类似的事情。我想更改无 cookie 模式,因为它取决于我的域和 IP(wap 代理 ip)。我完全迷路了。你得到更多的东西吗?提前谢谢 【参考方案1】:当所有其他方法都失败时,使用 Reflector 或 ILSpy 打开 .NET 实现,看看它们在做什么不同。
【讨论】:
他们没有实现 ISessionIDManager,他们实现了一个 HTTP 模块。【参考方案2】:从头开始创建自定义会话 ID 管理器似乎需要做很多工作。从 System.Web.SessionState.SessionIDManager 类继承并重写 CreateSessionID 方法呢?
public class MySessionIDManager : SessionIDManager, ISessionIDManager
public override string CreateSessionID(HttpContext context)
return System.Guid.NewGuid().ToString("N");
【讨论】:
有趣。应该管用。这也需要覆盖验证,但这没问题。 @StefanSteiger 你试过使用SessionIDManager
class 吗?以上是关于ASP.NET:为无 cookie 会话实现 ISessionIDManager?的主要内容,如果未能解决你的问题,请参考以下文章
如何同步表单身份验证 cookie 和 Asp.Net 会话的生命周期?
ASP.Net Core 2.1 API JWT 无 cookie 会话?