SessionID在Azure中(可能在Web场中)的不同实例之间进行更改

vto*_*ola 6 asp.net session web-farm azure cookieless

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

为了重现这个问题:

  1. 创建云项目.

  2. 添加ASP.NET Web角色.已有的代码可以.

  3. 打开 Default.aspx

  4. 添加控件以查看当前SessionID和按钮以进行回发

            <p><%= Session.SessionID %></p>
            <asp:Button ID="Button1" runat="server" Text="PostBack" onclick="Button1_Click" />
    
    Run Code Online (Sandbox Code Playgroud)
  5. 为按钮添加一个事件处理程序,它会稍微延迟响应:

    protected void Button1_Click(object sender, EventArgs e)
    {
        System.Threading.Thread.Sleep(150);
    }
    
    Run Code Online (Sandbox Code Playgroud)
  6. 打开 Web.Config

  7. 启用无Cookie会话:

    <system.web>
            <sessionState cookieless="true" />
    </system.web>
    
    Run Code Online (Sandbox Code Playgroud)
  8. 运行项目,快速点击"PostBack"按钮一段时间,注意地址栏中的会话ID.什么都没发生,会话ID始终是相同的:).停下来.

  9. 打开 ServiceConfiguration.csfg

  10. 启用四个实例:

    <Instances count="4" />
    
    Run Code Online (Sandbox Code Playgroud)
  11. 确保在Web.config中有一行与Visual Studio自动添加的机器密钥相关联.(在system.web的末尾).

  12. 重新运行项目,快速点击"Postback"按钮一段时间,然后注意地址栏中的会话ID.你会看到SessionID一段时间后的变化.

为什么会这样?据我所知,如果所有机器都共享machineKey,那么会话应该是相同的.使用cookie没有问题,问题显然是在使用无cookie会话时.

我最好的猜测是,当有多个实例,当SessionID一个实例生成一个实例WebRole,被拒绝并重新生成时,会发生错误.这没有意义,因为所有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);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

并使用它在WebConfig中更改此行:

    <sessionState cookieless="true" sessionIDManagerType="WebRole1.MySessionIDManager" />
Run Code Online (Sandbox Code Playgroud)

现在你可以看到SessionID它不会改变,无论你打多快和多久.如果取消注释这两行,您将看到ASP.NET如何创建新的sessionID,即使已经存在.

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

 Response.Redirect(Request.Url.AbsoluteUri.Replace(Request.Url.AbsolutePath, String.Empty));
Run Code Online (Sandbox Code Playgroud)

为什么这个事情发生在无cookie会话中?

我的解决方案有多可靠MySessionIDManager

亲切的问候.

更新:

  • 我尝试过这种解决方法: 用户指定的机器密钥被站点级自动配置覆盖,但问题仍然存在.

    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();
    }
    
    Run Code Online (Sandbox Code Playgroud)
  • 我也试过这个关于放置会话启动处理程序并添加一些数据,但没有运气.

    void Session_Start(object sender, EventArgs e)
    {
        Session.Add("dummyObject", "dummy");
    }
    
    Run Code Online (Sandbox Code Playgroud)

赏金!

Pan*_*vos 3

简而言之,除非您使用 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.CreateUnititializedItem确实支持 cookieless 模式,但它无法在private SessionStateStoreData GetSession(HttpContext context, string id, out bool Locked, 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(),
}
Run Code Online (Sandbox Code Playgroud)

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

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

if (actions == SessionStateActions.InitializeItem || stream.Length==0)
Run Code Online (Sandbox Code Playgroud)

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