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

时间:2011-10-01 22:49:48

标签: c# asp.net session session-state session-cookies

问题:

我正在编写自定义会话提供程序。 到目前为止,它的表现非常出色。 我决定添加一个自定义的ISessionIDManager来控制会话ID。

它已经适用于cookie会话。 但是,当我开始没有cookie,就像这样:

<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附带的会话管理器)并切换到cookieless时,URL如下所示:
http://localhost:52897/(S(sq2abm453wnasg45pvboee45))/DisplaySessionValues.aspx

并且没有HTTP 404 ...

我尝试在网址的括号中添加(S和)我的session-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

2 个答案:

答案 0 :(得分:4)

从头开始创建自定义会话ID管理器似乎需要做很多工作。如何继承System.Web.SessionState.SessionIDManager类并重写CreateSessionID方法?

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

答案 1 :(得分:0)

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