SessionID 在 Azure 中的不同实例中发生变化(可能在网络场中)

发布于 2024-10-19 11:45:00 字数 4732 浏览 8 评论 0原文

我的 Azure 项目有一个问题,该项目具有一个 WebRole,但有多个使用 cookieless 会话的实例。该应用程序不需要会话存储,因此它不使用任何会话存储提供程序,但我需要跟踪 SessionID。显然, SessionID 在 WebRole 实例中应该是相同的,但它突然改变且没有解释。我们使用 SessionID 来跟踪一些数据,因此它非常重要。

为了重现该问题:

  1. 创建一个云项目

  2. 添加ASP.NET Web 角色。其中已有的代码即可。

  3. 打开Default.aspx

  4. 添加一个控件以查看当前SessionID和一个按钮引起回发

     

    <%= Session.SessionID %>

  5. 为按钮添加一个事件处理程序,该事件处理程序将稍微延迟响应:

    protected void Button1_Click(object sender, EventArgs e)
    {
        系统.Threading.Thread.Sleep(150);
    }
    
  6. Open Web.Config

  7. 启用 cookieless 会话:

    ;
            
    
    
  8. 运行项目,并快速重复点击“PostBack” 按钮一段时间,注意地址栏中的会话 ID。什么也没发生,会话 ID 始终相同:)。停止它。

  9. 打开ServiceConfiguration.csfg

  10. 启用四个实例:

    <实例数=“4”/>
    
  11. 确保 Web.config 中有一行与 Visual Studio 自动添加的计算机密钥相关。 (在 system.web 的末尾)。

  12. 重新运行该项目,快速重复点击“回发”按钮一段时间,并注意地址栏中的会话 ID。一段时间后您将看到 SessionID 如何变化。

为什么会发生这种情况?据我所知,如果所有机器共享machineKey,那么它们之间的会话应该是相同的。使用 cookie 就没有问题,问题显然在于使用无 cookie 会话时。

我最好的猜测是,当有多个实例时,当一个 WebRole 中生成的 SessionID 转到另一个时,会发生错误,被拒绝并重新生成。这是没有意义的,因为所有 WebRole 都具有相同的 machineKey

为了找出问题并更清楚地看到它,我创建了自己的 SessionIDManager

public class MySessionIDManager : SessionIDManager
{
    public override string CreateSessionID(HttpContext context)
    {
        if (context.Items.Contains("AspCookielessSession"))
        {
            String formerSessionID = context.Items["AspCookielessSession"].ToString();

           // if (!String.IsNullOrWhiteSpace(formerSessionID) && formerSessionID != base.CreateSessionID(context))
               // Debugger.Break();

            return formerSessionID;
        }
        else
        {
            return base.CreateSessionID(context);
        }
    }
}

并使用它更改 WebConfig 中的这一行:

    <sessionState cookieless="true" sessionIDManagerType="WebRole1.MySessionIDManager" />

现在您可以看到 SessionID code> 不会改变,无论你击打的速度有多快、击打的时间有多长。如果取消注释这两行,您将看到 ASP.NET 如何创建一个新的 sessionID,即使已经有一个。

为了强制 ASP.NET 创建新会话,只需重定向到站点中的绝对 URL:

 Response.Redirect(Request.Url.AbsoluteUri.Replace(Request.Url.AbsolutePath, String.Empty));

为什么无 cookie 会话会发生这种情况?

我的 MySessionIDManager 解决方案的可靠性如何?

亲切的问候。

更新

  • 我已经尝试过此解决方法: 用户指定的计算机密钥 被站点级自动覆盖 配置,但是问题 依然屹立不倒。

    公共覆盖 bool OnStart()
    {
        // 有关处理配置更改的信息
        // 请参阅 http://go.microsoft.com/fwlink/?LinkId=166357 上的 MSDN 主题。
    
        使用 (var server = new ServerManager())
        {
            尝试
            {
                // 获取站点的Web配置
                var siteNameFromServiceModel = "网络"; // 更新您站点的站点名称。 
                var 站点名称 =
                    string.Format("{0}_{1}", RoleEnvironment.CurrentRoleInstance.Id, siteNameFromServiceModel);
                var siteConfig = server.Sites[siteName].GetWebConfiguration();
    
                // 获取appSettings部分
                var appSettings = siteConfig.GetSection("appSettings").GetCollection()
                    .ToDictionary(e => (string)e["key"], e => (string)e["value"]);
    
                // 重新配置机器密钥
                var machineKeySection = siteConfig.GetSection("system.web/machineKey");
                machineKeySection.SetAttributeValue("validationKey", appSettings["validationKey"]);
                machineKeySection.SetAttributeValue("验证", appSettings["验证"]);
                machineKeySection.SetAttributeValue("decryptionKey", appSettings["decryptionKey"]);
                machineKeySection.SetAttributeValue("解密", appSettings["解密"]);
    
                服务器.CommitChanges();
                _init=真;
            }
            抓住
            {
            }
        }
        返回base.OnStart();
    }
    
  • 我也尝试过这个关于 放一个 会话启动处理程序并添加 一些数据,但没有运气。

    void Session_Start(对象发送者,EventArgs e)
    {
        Session.Add("dummyObject", "dummy");
    }
    

赏金上来!

I have a problem with an Azure project with one WebRole but multiple instances that uses cookieless sessions. The application doesn't need Session storage, so it's not using any session storage provider, but I need to track the SessionID. Apparently, the SessionID should be the same accross the WebRole instances, but it changes suddently w/o explanation. We are using the SessionID to track some data, so it's very important.

In order to reproduce the issue:

  1. Create a Cloud Project.

  2. Add a ASP.NET Web Role. The code already in it will do.

  3. Open Default.aspx

  4. Add a control to see the current SessionID and a button to cause a postback

            <p><%= Session.SessionID %></p>
            <asp:Button ID="Button1" runat="server" Text="PostBack" onclick="Button1_Click" />
    
  5. Add a event handler for button that will delay the response a bit:

    protected void Button1_Click(object sender, EventArgs e)
    {
        System.Threading.Thread.Sleep(150);
    }
    
  6. Open Web.Config

  7. Enable cookieless sessions:

    <system.web>
            <sessionState cookieless="true" />
    </system.web>
    
  8. Run the project, and hit fast and repeteadly the "PostBack" button for a while giving attention to the session id in the address bar. Nothing happens, the session id is always the same :). Stop it.

  9. Open ServiceConfiguration.csfg

  10. Enable four instances:

    <Instances count="4" />
    
  11. Ensure that in the Web.config there is a line related with the machine key that has been added automatically by Visual Studio. (at the end of system.web).

  12. Rerun the project, hit fast and repeteadly the "Postback" button for a while and give attention to the session id in the address bar. You'll see how the SessionID changes after a while.

Why is this happening? As far as I know, if all machines share the machineKey, the session should be the same across them. With cookies there are no problems, the issue apparently is just when cookieless sessions are used.

My best guess, is that something wrong is happening when there are several instances, when the SessionID generated in one WebRole goes to another, is rejected and regenerated. That doesn't make sense, as all the WebRoles have the same machineKey.

In order to find out the problem, and see it more clearly, I created my own SessionIDManager:

public class MySessionIDManager : SessionIDManager
{
    public override string CreateSessionID(HttpContext context)
    {
        if (context.Items.Contains("AspCookielessSession"))
        {
            String formerSessionID = context.Items["AspCookielessSession"].ToString();

           // if (!String.IsNullOrWhiteSpace(formerSessionID) && formerSessionID != base.CreateSessionID(context))
               // Debugger.Break();

            return formerSessionID;
        }
        else
        {
            return base.CreateSessionID(context);
        }
    }
}

And to use it change this line in the WebConfig:

    <sessionState cookieless="true" sessionIDManagerType="WebRole1.MySessionIDManager" />

Now you can see that the SessionID doesn't change, no matter how fast and for how long you hit. If you uncomment those two lines, you will see how ASP.NET is creating a new sessionID even when there is already one.

In order to force ASP.NET to create a new session, just a redirect to an absolute URL in your site:

 Response.Redirect(Request.Url.AbsoluteUri.Replace(Request.Url.AbsolutePath, String.Empty));

Why is this thing happening with cookieless sessions?

How reliable is my solution in MySessionIDManager ?

Kind regards.

UPDATE:

  • I've tried this workaround:
    User-Specified Machine Keys
    Overwritten by Site-Level Auto
    Configuration
    , but the problem
    still stands.

    public override bool OnStart()
    {
        // For information on handling configuration changes
        // see the MSDN topic at http://go.microsoft.com/fwlink/?LinkId=166357.
    
        using (var server = new ServerManager())
        {
            try
            {
                // get the site's web configuration
                var siteNameFromServiceModel = "Web"; // update this site name for your site. 
                var siteName =
                    string.Format("{0}_{1}", RoleEnvironment.CurrentRoleInstance.Id, siteNameFromServiceModel);
                var siteConfig = server.Sites[siteName].GetWebConfiguration();
    
                // get the appSettings section
                var appSettings = siteConfig.GetSection("appSettings").GetCollection()
                    .ToDictionary(e => (string)e["key"], e => (string)e["value"]);
    
                // reconfigure the machine key
                var machineKeySection = siteConfig.GetSection("system.web/machineKey");
                machineKeySection.SetAttributeValue("validationKey", appSettings["validationKey"]);
                machineKeySection.SetAttributeValue("validation", appSettings["validation"]);
                machineKeySection.SetAttributeValue("decryptionKey", appSettings["decryptionKey"]);
                machineKeySection.SetAttributeValue("decryption", appSettings["decryption"]);
    
                server.CommitChanges();
                _init = true;
            }
            catch
            {
            }
        }
        return base.OnStart();
    }
    
  • I've also tried this about put a
    session start handler
    and add
    some data, but no luck.

    void Session_Start(object sender, EventArgs e)
    {
        Session.Add("dummyObject", "dummy");
    }
    

Bounty up!

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

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

发布评论

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

评论(3

唐婉 2024-10-26 11:45:00

简而言之,除非您使用 cookie 或会话提供程序,否则会话 ID 无法从一个 Web 角色实例传递到另一个 Web 角色实例。您提到的帖子说,如果您不使用 cookie 或会话存储,则 SessionID 不会在 Web 角色之间保持相同。
检查此上一个问题,了解处理 Azure 中状态存储的方法,例如使用表存储

machineKey 与会话或应用程序域无关,它是用于 加密、解密、验证身份验证和视图状态数据。要验证这一点,请使用 Reflector 打开 SessionIDManager.CreateSessionID。您将看到 ID 值只是一个编码为字符串的随机 16 字节值。

AspCookielessSession 值已由 SessionIDManager 在 GetSessionID 方法中检查,而不是 CreateSessionID,因此在执行代码之前检查已完成。由于默认的会话状态模式是 InProc,因此单独的 Web 角色将无法验证会话密钥,因此它们会创建一个新的会话密钥。

事实上,角色随时可能迁移到不同的物理机,在这种情况下其状态将会丢失。 SQL Azure 团队的这篇文章介绍了一种使用的方法SQL Azure 正是出于这个原因来存储状态

编辑 我终于让 TableStorageSessionStateProvider 在 cookieless 模式下工作!

虽然 TableStorageSessionStateProvider 通过重写 SessionStateStoreProviderBase 确实支持 cookieless 模式。 CreateUnititializedItem,它无法在private SessionStateStoreData GetSession(HttpContext context, string id, out boollocked, out TimeSpan lockAge,out object lockId, out SessionStateActions actions,bool Exclusive)中正确处理空会话 。解决方案是,如果在底层 Blob 存储中找不到数据,则返回空的 SessionStateStoreData。

该方法有 145 行,所以我不会在这里粘贴。搜索以下代码块

if (actions == SessionStateActions.InitializeItem) 
{
     // Return an empty SessionStateStoreData                    
     result = new SessionStateStoreData(new SessionStateItemCollection(),
}

该块在创建新会话时返回一个空会话数据对象。不幸的是,空数据对象没有存储到 blob 存储中。

将第一行替换为以下行,使其在 blob 为空时返回空对象:

if (actions == SessionStateActions.InitializeItem || stream.Length==0)

Long stroy Short 只要提供程序支持,cookieles 会话状态就可以工作。不过,您必须决定使用无 cookie 状态是否适合使用示例提供程序。也许 vtortola 应该检查 AppFabric 缓存 CTP。它包含开箱即用的 ASP.NET 提供程序,速度更快,而且肯定比示例提供程序有更好的支持。甚至还有一个关于如何设置会话状态的分步教程它。

In short, unless you use cookies or a session provider there is no way for the session id to pass from one web role instance to the other. The post you mention says that the SessionID does NOT stay the same across web roles if you don't use cookies or session storage.
Check this previous question for ways to handle state storage in Azure, e.g. using Table Storage

The machineKey has nothing to do with sessions or the application domain, it is the key used to encrypt,decrypt,validate authentication and viewstate data. To verify this open SessionIDManager.CreateSessionID with Reflector. You will see that the ID value is just a random 16-byte value encoded as a string.

The AspCookielessSession value is already checked by SessionIDManager in the GetSessionID method, not CreateSessionID so the check is already finished before your code gets executed. Since the default sessionstate mode is InProc it makes sence that separate web roles will not be able to validate the session key so they create a new one.

In fact, a role may migrate to a different physical machine at any time, in which case its state will be lost. This post from the SQL Azure Team describes a way to use SQL Azure to store state for exactly this reason.

EDIT I finally got TableStorageSessionStateProvider to work in cookieless mode!

While TableStorageSessionStateProvider does support cookieless mode by overriding SessionStateStoreProviderBase.CreateUnititializedItem, it fails to handle empty sessions properly in private SessionStateStoreData GetSession(HttpContext context, string id, out bool locked, out TimeSpan lockAge,out object lockId, out SessionStateActions actions,bool exclusive). The solution is to return an empty SessionStateStoreData if no data is found in the underlying blob storage.

The method is 145 lines long so I won't paste it here. Search for the following code block

if (actions == SessionStateActions.InitializeItem) 
{
     // Return an empty SessionStateStoreData                    
     result = new SessionStateStoreData(new SessionStateItemCollection(),
}

This block returns an empty session data object when a new session is created. Unfortunately the empty data object is not stored to the blob storage.

Replace the first line with the following line to make it return an empty object if the blob is empty:

if (actions == SessionStateActions.InitializeItem || stream.Length==0)

Long stroy short cookieles session state works as long as the provider supports it. You'll have to decide whether using cookieless state justifies using a sample provider though. Perhaps vtortola should check the AppFabric Caching CTP. It includes out-of-the-box ASP.NET providers, is a lot faster and it definitely has better support than the sample providers. There is even a step-by-step tutorial on how to set session state up with it.

記柔刀 2024-10-26 11:45:00

听起来很棘手。

我有一个建议/问题要问你。不知道这是否有帮助 - 但听起来你已经准备好尝试任何事情了!

听起来好像新机器上的会话管理器正在检查中央会话存储提供程序,当它发现会话存储为空时,它会发出新的会话密钥。

我认为解决方案可能来自:
- 使用上面的 Session_Start 来将某些内容插入到会话存储中
- 另外将一些描述的持久会话存储提供程序插入到 web.config 中 - 例如,一些最旧的 Azure 示例提供基于表的提供程序,或者一些较新的示例提供 AppFabric 缓存解决方案。

我知道您的设计没有使用会话存储,但也许您需要放入一些东西(有点像您的 Session_Start),另外您需要定义除进程内会话管理之外的其他东西。

或者,您需要围绕 ASP.NET 会话以外的其他内容重新设计您的应用程序。

希望有帮助 - 祝你好运!

Sounds tricky.

I have one suggestion/question for you. Don't know if it will help - but you sound like you're ready to try anything!

It sounds like maybe the session manager on the new machine is checking the central session storage provider and, when it finds that the session storage is empty, then it's issuing a new session key.

I think a solution may come from:
- using Session_Start as you have above in order to insert something into Session storage
- plus inserting a persistent Session storage provider of some description into the web.config - e.g. some of the oldest Azure samples provide a table based provider, or some of the newer samples provide an AppFabric caching solution.

I know your design is not using the session storage, but maybe you need to put something in (a bit like your Session_Start), plus you need to define something other than in-process session management.

Alternatively, you need to redesign your app around something other than ASP.NET sessions.

Hope that helps - good luck!

一花一树开 2024-10-26 11:45:00

我遇到了同样的问题,经过大量研究和调试,我发现问题的发生是因为Azure SDK中的“虚拟服务器”将网站映射到IIS元数据库中的不同路径。 (您可以通过 Request.ServerVariables["APPL_MD_PATH"] 看到这一点。)

我现在才发现这一点,但想发布它,以便人们可以开始测试它。我的理论是,一旦将其发布到 Azure 本身,这个问题可能就会消失。我会更新我发现的任何结果。

I experienced the same problem and after much research and debugging I found that the issue occurred because the "virtual servers" in the Azure SDK map the websites to different paths in the IIS metabase. (You can see this through through Request.ServerVariables["APPL_MD_PATH"].)

I just found this out now but wanted to post this so people could get working on testing it. My theory is that this problem may go away once it's published out to Azure proper. I'll update with any results I find.

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