我的web应用程序正在使用WIF根据用户自己的STS对用户进行身份验证(我无法控制他们的STS是如何设置的,我只给他们联合元数据的url)。
我的web应用程序运行在2个负载平衡器上,后面有2台服务器,我还使用了超时1小时的粘性会话,并且两台机器共享同一个机器密钥,我还在IIS中将LoadUserProfile设置为true。
当只有一个用户使用唯一的STS登录时,它似乎可以正常工作,但只要有一个以上的用户,我就可以看到以下错误在短时间内多次记录在服务器上。
Key not valid for use in specified state.
Stack Trace: at System.Security.Cryptography.ProtectedData.Unprotect(Byte[] encryptedData, Byte[] optionalEntropy, DataProtectionScope scope) at Microsoft.IdentityModel.Web.ProtectedDataCookieTransform.Decode(Byte[] encoded)rn at System.Security.Cryptography.ProtectedData.Unprotect(Byte[] encryptedData, Byte[] optionalEntropy, DataProtectionScope scope) at Microsoft.IdentityModel.Web.ProtectedDataCookieTransform.Decode(Byte[] encoded)
如何解决此错误,或在诊断此问题时提供任何帮助
对于将来寻找相同问题的人,这里的信息解决了我的问题。http://weblogs.asp.net/cibrax/the-system-cannot-find-the-file-specified-error-in-the-wif-fam-module(以下文章的副本)
作为WIF的一部分提供的联合身份验证模块(FAM)默认情况下使用DPAPI保护会话cookie在被动场景中不被篡改。正如我在过去提到的,这种技术大大简化了整个解决方案的初始部署,因为不需要额外配置,自动生成的DPAPI密钥用于保护cookie,因此这可能是在WSE、WCF和现在的WIF中使用它作为默认保护机制的原因。
然而,从我的角度来看,这种技术有一些严重的缺点,这使得它在真实的企业场景中毫无用处。
如果依赖FAM对用户进行身份验证的web应用程序托管在IIS中。运行IIS进程的帐户需要创建一个配置文件才能使用DPAPI。解决方法是使用该帐户登录到计算机以创建初始配置文件,或者运行一些脚本自动创建。
DPAPI不适用于web场场景,因为机器密钥用于保护cookie。如果cookie由一个密钥保护,则必须将以下请求发送到同一台机器。解决方法可以是使用粘性会话,这样来自同一台计算机的所有用户请求都由服务器场上的同一台机器处理。
幸运的是,WIF已经提供了一些内置类,用一种基于X509证书的RSA密钥的保护机制来取代这种默认机制。
"SecuritySessionHandler"是WIF中负责将身份验证会话跟踪到cookie中的处理程序。默认情况下,该处理程序接收一些将转换应用于cookie内容的内置类,例如DeflatCookieTransform和ProtectedDataCookieTransform(用于使用DPAPI保护内容)。还有另外两个根本不使用的CookieTransform派生类,RSAEncryptionCookieTransform和RSASignatureCookietransfer类非常方便地启用企业场景。这两个类都接收用于加密或签名cookie内容的RSA密钥或X509证书。
因此,您可以将以下代码放在global.asax文件中,用使用X509证书的cookie转换来替换默认的cookie转换。
protected void Application_Start(object sender, EventArgs e)
{
FederatedAuthentication.ServiceConfigurationCreated += new EventHandler<Microsoft.IdentityModel.Web.Configuration.ServiceConfigurationCreatedEventArgs>(FederatedAuthentication_ServiceConfigurationCreated);
}
void FederatedAuthentication_ServiceConfigurationCreated(object sender, Microsoft.IdentityModel.Web.Configuration.ServiceConfigurationCreatedEventArgs e)
{
var cookieProtectionCertificate = CertificateUtil.GetCertificate(StoreName.My,
StoreLocation.LocalMachine, "CN=myTestCert");
e.ServiceConfiguration.SecurityTokenHandlers.AddOrReplace(
new SessionSecurityTokenHandler(new System.Collections.ObjectModel.ReadOnlyCollection<CookieTransform> (
new List<CookieTransform>
{
new DeflateCookieTransform(),
new RsaEncryptionCookieTransform(cookieProtectionCertificate),
new RsaSignatureCookieTransform(cookieProtectionCertificate)
})
));
}
代码中唯一需要更改的部分是它试图在服务器上查找证书位置的位置。
var cookieProtectionCertificate = CertificateUtil.GetCertificate(StoreName.My, StoreLocation.LocalMachine, "CN=myTestCert");