在asp.net里面,View State使用較為廣泛。它作為一個(gè)隱藏字段,可以幫助服務(wù)端”記住“客戶端的改變,這樣客戶端 收到服務(wù)器對PostBack的響應(yīng)后,仍然可以展現(xiàn)在PostBack之前設(shè)定的值 (具體參見http://msdn.microsoft.com/en-us/library/bb386448(v=vs.100).aspx )
<input type="hidden" name="__VIEWSTATE" id="__VIEWSTATE" value="..." />
為了防止惡意客戶端的PostBack里的ViewState被解讀,ASP.NET會(huì)用消息驗(yàn)證碼(MAC)來檢查每個(gè)ViewState。可是一旦服務(wù)器無法正確解釋正常客戶端PostBack回來的ViewState時(shí),整個(gè)應(yīng)用都會(huì)停止工作。比如出現(xiàn) ”Validation of viewstate MAC failed” 的錯(cuò)誤。
通常來說,一旦這樣的錯(cuò)誤出現(xiàn),首先會(huì)考慮以下幾種情況:
1. 是不是有多臺(tái)Web Server在負(fù)載均衡情況下運(yùn)行。如果是的話,需要各臺(tái)服務(wù)器使用相同的MAC進(jìn)行ViewState的加密和解密工作。否則如果這個(gè)負(fù)載均衡環(huán)境沒有完全做的session Affinity,這種錯(cuò)誤就會(huì)出現(xiàn)。
2. 測試本機(jī)訪問是否也有這種錯(cuò)誤。如果是的話,除了嘗試重新產(chǎn)生新的MachineKey (參見http://blogs.msdn.com/b/amb/archive/2012/07/31/easiest-way-to-generate-machinekey.aspx ),也可以用PRocess Monitor在復(fù)現(xiàn)問題的時(shí)候跟蹤文件和注冊表的訪問,看看是不是因?yàn)閃3WP.exe缺少權(quán)限而不能獲取和MachineKey相關(guān)的信息。
3. 在客戶端和服務(wù)器端抓取網(wǎng)絡(luò)包,比較ViewState是否被中間設(shè)備改動(dòng)。這種情況不多見,但是也遇到過。算是復(fù)雜的一種情況。如果連接是SSL的,抓包沒有辦法查看,客戶端就要使用Fiddler,而服務(wù)端需要采取額外診斷日志或者Debug的方法。
4. 和具體代碼相關(guān),尤其是對ViewStateUserKey有特殊設(shè)置。
這里談到的是一個(gè)在一個(gè)大型的生產(chǎn)應(yīng)用環(huán)境里遇到的實(shí)際問題,和上面的情況有關(guān),但有些有趣的變化。
這個(gè)環(huán)境里面有多臺(tái)Web 服務(wù)器,采用了負(fù)載均衡方式。在客戶試圖登錄時(shí)(login.aspx),總是會(huì)遇到”Validation of viewstate MAC failed”的錯(cuò)誤。
起初,懷疑是不同機(jī)器上WebAppication的MachineKey不一樣引起的。檢查了Web.Config里的配置,各個(gè)機(jī)器都是一樣:
<system.web><machineKey decryptionKey="6284D74F8D9745C38712047622FFA047B02CA5C4049FB74E,IsolateApps" validationKey="137B974DC38A910D946AAF3ADF1D0386072170236F39C8165098035126FE7DFDF68C7BD3646052CE1769A47A45F098A65CEC3089523543370DD37830A5B2D13,IsolateApps" /></system.web>
而且負(fù)載均衡也設(shè)置了Class C的Session Affinity
后來發(fā)現(xiàn)這個(gè)問題即使本機(jī)訪問也會(huì)出現(xiàn)。把應(yīng)用程序池身份改為Admin后,問題同樣。表明和權(quán)限無關(guān)。重新創(chuàng)建MachineKey,也沒有變化。
這時(shí)需要關(guān)注代碼。獲取了頁面代碼做Review. 在Login頁面的Page_Init里面, 看到ViewStateUserKey
protected void Page_Init(object sender, EventArgs e){ this.ViewStateUserKey = this.Session.SessionID; }表面上看這樣的寫法也沒有什么問題。
在IE上啟用了Fiddler (連接是SSL) 后,發(fā)現(xiàn)客戶端的PostBack里面沒有Cookie的信息。這就是問題出現(xiàn)的直接原因:
a. 在第一次訪問Login頁面時(shí),用一個(gè)隨機(jī)的SessionID A被嵌入了ViewState里面
b. 在PostBack時(shí),由于沒有Cookie傳回來(ASP.NET SessionID缺省存放在Cookie里),服務(wù)器就判斷道客戶端沒有SessionID, 于是又使用一個(gè)新的SessionID B做ViewStateUserKey. 這樣,從ViewState里面解出的是上次的SessionID A, 和新的不匹配。錯(cuò)誤就出現(xiàn)了。
可是客戶端為什么不發(fā)送Cookie呢?
原來這個(gè)服務(wù)器第一次回復(fù)時(shí),對客戶端的HTTP Header里面根本沒有Set-Cookie字段。
這是沒有賦予Session 變量時(shí)ASP.NET的缺省行為 ( SessionID每次請求都會(huì)形成,但是未必會(huì)發(fā)送Set-Cookie 來讓客戶端保留這個(gè)SessioID,除非有賦予Session變量的行為)
于是解決這個(gè)問題的直接方法就是在這個(gè)Page_Init里面第一時(shí)間加一個(gè)語句:
protected void Page_Init(object sender, EventArgs e){ this.ViewStateUserKey = this.Session.SessionID; session("forViewSate")="value" }這樣服務(wù)器把SessionID在”Set-Cookie”里發(fā)送回去,客戶端也可以用Cookie保留SessionID。問題就立刻解決了。
更多參考:
微軟技術(shù)文章討論如何處理” ”Validation of viewstate MAC failed”:
http://support.microsoft.com/kb/2915218
|
新聞熱點(diǎn)
疑難解答
圖片精選
網(wǎng)友關(guān)注