所以,这个简直太棒了。
当以下所有条件都为真时,就会出现该问题:
- 您正在运行 Windows Server 2003 (IIS 6.0) 和 ASP.NET 2.0 网站。
- 该网站配置为使用 Web Gardens,其中工作进程的最大数量大于 1。因此,您已将应用程序配置为使用进程外会话存储;在这种情况下,ASP.NET 状态服务在本地计算机上运行。
- 应用程序池标识未设置为 NETWORK SERVICE,而是设置为您根据部署最佳实践创建的自定义、低权限用户帐户。
- 您运行一个更新 .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
存在任何条目(实际上,它甚至无法读取它),生成自己的并开始使用它来散列发送到 ASP.NET 状态服务的数据。它试图将这个新的机器密钥保存到注册表项,但静默失败。工作进程 B 启动,发现不AutoGenKey
存在任何条目,生成自己的条目并开始使用它来散列数据……你知道这是怎么回事。
结果是现在您拥有使用三个不同机器密钥散列的会话数据。尽管会话标识符的数据存在,但三分之二的工作进程会因为它使用自己的密钥而将其拒绝为无效/被篡改。
您可以通过machineKey
在web.config
文件中明确设置自定义来解决此问题。
或者您可以在命令提示符下重新运行aspnet_regiis.exe -ga MachineName\ApplicationPoolUserName
以修复损坏的权限。
你的问题解决了。是时候去睡觉了。
6 月 30 日更新:根据我在Microsoft Connect上对此问题的报告,Microsoft 表示他们已经修复了安装程序,因此从升级到 .NET 4 开始不会发生这种行为。它仍然可能发生在所有未来的 3.0/3.5 升级中,所以我将保留这个问题/答案。