所以,这真是太棒了。
当满足以下所有条件时,就会出现此问题:
- 您正在运行 Windows Server 2003 (IIS 6.0) 和 ASP.NET 2.0 网站。
- 该网站配置为使用网络花园,其中工作进程的最大数量大于 1。因此,您已将应用程序配置为使用进程外会话存储;在这种情况下,ASP.NET State Service 在本地计算机上运行。
- 应用程序池标识未设置为 NETWORK SERVICE,而是设置为您根据应用程序创建的自定义低权限用户帐户。部署最佳实践 http://msdn.microsoft.com/en-us/library/ms998297.aspx.
- 您运行更新 .NET 框架的安装程序;就我而言,这是从 .NET 3.0 到 .NET 3.5 SP1 的更新。
当升级完成并重新启动服务器时,您会发现刷新页面时会话变量经常丢失,因为只有三分之一的机会获得为原始请求提供服务的原始工作进程。但这并不重要,因为您正在使用 ASP.NET 状态服务。什么坏了?
使用 ASP.NET 状态服务时,ASP.NET 使用一个称为machineKey
加密和/或散列要存储的所有会话数据(我不知道它是加密还是散列或两者,但这对于本次讨论来说不是一个重要的区别)。这样,当任何工作进程使用会话标识符向服务请求数据时,就可以确保数据在存储在外部数据源中时没有被篡改。
如果您在网络农场中,那么您可能有一个静态的machineKey
定义在你的web.config
文件,则不会出现此问题。但对于单服务器网络花园场景,您可能依赖于默认的machineKey
设置,即设置为AutoGenerate,IsolateApps
适用于 ASP.NET 2.0 应用程序。这意味着 ASP.NET 自动生成一个对于您的应用程序池来说唯一的计算机密钥。它根据某种算法重新生成该密钥,但这对于本次讨论并不重要。
生成的值通常存储在注册表中HKLM\SOFTWARE\Microsoft\ASP.NET\2.0.50727.0\AutoGenKeys\{SID of the Application Pool Identity}
。但是 .NET Framework 安装程序错误地(我确实相信这是一个错误)破坏了此注册表项,并且雪上加霜的是,它重置了此注册表项的权限,以便您的自定义应用程序池标识在运行时无法写入注册表项创建新的机器密钥。
结果是,在网络园中启动的每个工作进程都使用自己及时生成的机器密钥的内存副本,从而有效地意外创建了一个网络场场景。例如,工作进程 A 启动,发现没有AutoGenKey
条目存在(事实上,它甚至不能read它),生成自己的并开始使用它来散列发送到 ASP.NET 状态服务的数据。它尝试将此新计算机密钥保存到注册表项,但失败了。工作进程 B 启动,发现没有AutoGenKey
条目存在,生成自己的条目并开始使用that散列数据......你知道这是怎么回事。
结果是现在您拥有使用三个不同机器密钥进行哈希处理的会话数据。尽管会话标识符的数据存在,但三分之二的工作进程会将其拒绝为无效/被篡改,因为它使用自己的密钥。
您可以通过显式设置自定义来解决此问题machineKey
在你的web.config
file.
或者你可以重新运行aspnet_regiis.exe -ga MachineName\ApplicationPoolUserName
在命令提示符下修复损坏的权限。
你的问题已经解决了。是时候去睡觉了。
6月30日更新:根据我关于此问题的报告微软连接 http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=412881,微软表示他们已经修复了安装程序,这样从升级到 .NET 4 开始就不会发生这种情况。对于未来的所有 3.0/3.5 升级,这种情况仍然可能发生,所以我将保留这个问题/答案。