問題産生根源:
當然,其實應該需要保持線上所有機器環境一緻!可是,寫了一個小程式。使用的是4.5,aysnc/await實在太好用了,真心不想把代碼修改回去。
so,動了念頭,在這台伺服器上裝個4.5,ms不是說了麼,4.5和4.0是高度相容的。。。。。。
問題現象:
一台伺服器在安裝.net framework 4.5 之後,在該伺服器所部署的網站(使用.net framework 4,未修改任何配置,分布式環境),
網站在這台伺服器上登入之後,打開其他伺服器的任何站點,form驗證過不去,導緻重複登入,反之亦然.
問題分析:
為什麼會導緻重複登入問題?
很簡單能推斷出是在這個機器上安裝了4.5 ,某些元件的變動,導緻form驗證的加解密方式有變動.使得2台機器生成的登入cookie内容不一緻,不能互相解析.
能影響到.net對form加解密産生不同作用的地方無非2個.
1.本身代碼的bug,相容性問題問題。
2.配置影響(如web.config中的authentication,machineKey等).
1嘛,基本不可能,ms沒這麼渣,那就隻能從2下手,但是具體什麼配置影響到,就不得而知了.
通過參數配置,如果有改變,那對加解密産生的改變都是相符的. so,我們分析一下加密的方法,找出不同,通過參數來相容這些修改.那問題就解決了.
form驗證相關的方法,都在System.Web.Security.FormsAuthentication中.
通過調用加密方法在4.5上生成加密字元串,丢到4.0的機器上解密,不通過,提示加密字元串驗證不通過.
so,我們看看加密方法中做了什麼
加密方法:
省略部分代碼,剩下的關鍵代碼。
public static string Encrypt(FormsAuthenticationTicket ticket)
{
return Encrypt(ticket, true);
}
internal static string Encrypt(FormsAuthenticationTicket ticket, bool hexEncodedTicket)
{
byte[] clearData = MakeTicketIntoBinaryBlob(ticket);
if ((_Protection == FormsProtectionEnum.All) || (_Protection == FormsProtectionEnum.Encryption))
{
clearData = MachineKeySection.EncryptOrDecryptData(true, clearData, null, 0, clearData.Length, false, false, IVType.Random);
}
}
return CryptoUtil.BinaryToHex(clearData);
}
然後我們繼續深入到MakeTicketIntoBinaryBlob中檢視
private static byte[] MakeTicketIntoBinaryBlob(FormsAuthenticationTicket ticket)
{
if (!AppSettings.UseLegacyFormsAuthenticationTicketCompatibility)
{
return FormsAuthenticationTicketSerializer.Serialize(ticket);
}
........................
}
對比4,4.5中MakeTicketIntoBinaryBlob方法代碼,發現4.5的源代碼中多了AppSettings.UseLegacyFormsAuthenticationTicketCompatibility這麼一個開關配置.
系統預設值為flase,so.在4.5中得到的加密字元串來自FormsAuthenticationTicketSerializer.Serialize(ticket).而4中是在後續代碼中.
so,增加配置<add key="aspnet:UseLegacyFormsAuthenticationTicketCompatibility" value="true" /> 相容到這部分.
然後,我們繼續看MachineKeySection.EncryptOrDecryptData(true, clearData, null, 0, clearData.Length, false, false, IVType.Random);
internal static byte[] EncryptOrDecryptData(bool fEncrypt, byte[] buf, byte[] modifier, int start, int length, bool useValidationSymAlgo, bool useLegacyMode, IVType ivType)
{
return EncryptOrDecryptData(fEncrypt, buf, modifier, start, length, useValidationSymAlgo, useLegacyMode, ivType, !AppSettings.UseLegacyEncryption);
}
很熟悉,又看到!AppSettings.UseLegacyEncryption開關配置.進入EncryptOrDecryptData方法中能看到這個參數影響到使用不同的加密方式.
同上,增加配置<add key="aspnet:UseLegacyEncryption" value="true" />相容到這部分.
再次調用Encrypt方法生成加密字元串,丢到4.0機器上.哇,能解密成功了.
當然,實際解決起來,走了不少彎路,周五晚上發現問題,查了一晚上,未果,但是有思路了.後又周一查了2個小時,終于搞定這個問題.
相關說明:
有關安全更新 2638420 的部署指南,請參見 MS11-100 如何配置 ASP.NET 中的舊加密模式看了上面一個,好傻...如果發現問題的時候,之後搜尋asp.net 舊加密方式. 馬上解決...