开发者

ASP.NET: Implementing ISessionIDManager for cookieless sessions?

开发者 https://www.devze.com 2023-04-10 04:52 出处:网络
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.

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.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号