ASP.NET: Implementing ISessionIDManager for cookieless sessions?
Question:
I'm writing a custom session provider. So far it works excellently. I decided I wanted to add a customized ISessionIDManager, to control the session id.
It already works fine for cookie sessions. But when I swich to cookieless, like this:
<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>
Then it redirects to:
http://localhost:52897/(77bb065f-d2e9-4cfc-8117-8b89a40e00d8)/default.aspx and this throws HTTP 404. I understand why, as there is no such folder. But when you use the default session manager (the one that ships with asp.net), and switch to cookieless, the URL looks like this: http://localhost:52897/(S(sq2abm453wnasg45pvboee45))/DisplaySessionValues.aspx and there is no HTTP 404... I tried adding the (S and ) to my session-id in brackets in the url, but that didn't help. What am I missing ?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://stackoverflow.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 !
开发者_JAVA技巧 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
Creating a custom session id manager from scratch seems like a lot of work. What about inheriting from System.Web.SessionState.SessionIDManager class and overriding the CreateSessionID method?
public class MySessionIDManager : SessionIDManager, ISessionIDManager
{
public override string CreateSessionID(HttpContext context)
{
return System.Guid.NewGuid().ToString("N");
}
}
When all else fails, crack open the .NET implementation with Reflector or ILSpy and see what they are doing different.
精彩评论