當你準備將Web應用程式從ASP.NET 1.1升級到ASP.NET 2.0,你將面對這樣一個cookie問題:在ASP.NET 1.1應用程式中用戶端保存的所有cookie將會失效。
博客園也遇到了這樣的問題,對博客園來說,意味著所有使用cookie的用戶都需要重新登錄,雖然這不是一個很大的問題,但的確給大家帶來了麻煩,如果忘記了密碼,將更加麻煩。
對於一個非常重視使用者滿意度的網站來說,應該努力解決這個問題。博客園希望盡可能減少升級帶來的影響,所以這兩天我一直在研究這個問題並找到了解決方案。
問題的原因是:當程式從ASP.NET 1.1升級到於ASP.NET 2.0後,ASP.NET 2.0使用新的演算法與金鑰對客戶端發送過來的cookie進行解密,這樣導致ASP.NET中產生的cookie在ASP.NET 2.0中失效。在ASP.NET 1.1中,使用3DES演算法對cookie的內容進行加密,而在ASP.NET 2.0中預設使用Advanced Encrypted Standards (AES)演算法進行解密,這是造成問題的原因之一,透過對應的設定可以將ASP.NET 2.0中將cookie加密演算法改為3DES,只需在web.config中加上:<machineKey decryption="3DES"/>。但這樣做之後問題依然存在,因為解密時除了需要相同的演算法,還需要相同的金鑰。如果沒有在machineKey中指定金鑰,ASP.NET 2.0會預設會使用隨機產生的金鑰,這個隨機金鑰由System.Web.HttpRuntime.SetAutogenKeys()產生並儲存於System.Web.HttpRuntime.s_autogenKeys中,透過反射你可以獲得這個值。 ASP.NET 1.1的machineKey是在machine.config中設定的,預設也是使用隨機金鑰:
<machineKey validationKey="AutoGenerate,IsolateApps" decryptionKey="AutoGenerate,IsolateApps" validation="SHA1"/>。
問題就出在不同的隨機密鑰。如果你在原來的ASP.NET 1.1中指定了金鑰,那就不存在這個問題了,但一般在使用Web farm時,才會考慮這一點。所以通常情況都是使用隨機密鑰。 ASP.NET會為不同的應用程式產生不同的隨機金鑰,這個客戶端cookie失效問題會出一在很多情況下,例如:重裝系統、將ASP.NET應用程式移至另外一台計算機,將Web應用程式移到不同的虛擬目錄等等。
如何解決這個問題呢?
原理很簡單,只要我們知道在ASP.NET 1.1中隨機產生的金鑰的值,然後在ASP.NET 2.0應用程式的web.config中進行指定就行了,這裡的金鑰有兩個:一個是加密金鑰decryptionKey,一個是雜湊計算金鑰validationKey(防止cookie被中途篡改)。假如我們知道金鑰分別為:X、Y,那在web.config
進行如下設定就能解決問題:
<machineKey validationKey="X" decryptionKey="Y" decryption="3DES"/>
而難題就在於如何得到ASP.NET 1.1中隨機產生的金鑰的值。金鑰儲存在LSA(Windows Local Security Authority)中,但我找不到可以從LSA取得金鑰的方法。
由於部落格園主要是解決登入cookie的問題,而這個cookie是在System.Web.Security.FormsAuthentication. SetAuthCookie(string userName, bool createPersistentCookie)中產生的,所以我就從ASP.NET 1.1的System.Web.Security .FormsAuthentication的源代碼下手,發現了System.Web.Configuration.MachineKey,經過進一步對MachineKey的源代碼進行研究,在MachineKey的MachineKeyConfig中發現了兩個密鑰分別存在於s_validationKey與s_oDes這兩個私有靜態成員中(發現這個費了不少功夫),validationKey的值直接儲存於s_validationKey中,而decryptionKey儲存於s_oDes.Key中。由於MachineKey是internal class,MachineKeyConfig是私人類型,那兩個成員是私人靜態成員,無法直接存取。這時,該是.NET中強大的反射功能發揮作用的時候了。透過反射得到這兩個值,需要注意的是這兩個值的類型是Byte[],透過測試發現直接轉換成字串產生的金鑰無效,需要透過反射來呼叫System.Web.Configuration.MachineKey.ByteArrayToHexString (Byte[], Int32) 轉換成字串。
今天晚上終於解決了這個問題,好興奮!中途幾次想放棄,但想到在博客園程式升級到ASP.NET 2.0後,會因為這個問題給很多人帶來麻煩,雖然只需要重新登入一下就行了,但我還是覺得要解決這個問題,做程式開發不就是盡可能為使用者帶來方便嗎?
解決了這個問題就為部落格園網站升級到ASP.NET 2.0做了進一步的準備。
來源:dudu-快樂程式設計師