ASP.NET:为无 cookie 会话实现 ISessionIDManager?

发布于 2024-12-07 08:14:53 字数 12820 浏览 1 评论 0原文

问题:

我正在编写一个自定义会话提供程序。 到目前为止,效果非常好。 我决定添加一个自定义的 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 和 ) 添加到 URL 括号中的会话 ID,但这没有帮助。


我缺少什么?

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 !

                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

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 !

                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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

段念尘 2024-12-14 08:14:53

从头开始创建自定义会话 ID 管理器似乎需要大量工作。从 System.Web.SessionState.SessionIDManager 类继承并重写 CreateSessionID 方法怎么样?

public class MySessionIDManager : SessionIDManager, ISessionIDManager
{
    public override string CreateSessionID(HttpContext context)
    { 
        return System.Guid.NewGuid().ToString("N");
    }
}

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");
    }
}
意中人 2024-12-14 08:14:53

当所有其他方法都失败时,请使用 Reflector 或 ILSpy 破解 .NET 实现,看看它们做了什么不同的事情。

When all else fails, crack open the .NET implementation with Reflector or ILSpy and see what they are doing different.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文