我們項目的網站的移動版是基於Asp.Net平台開發的,用戶登錄也是基於Asp.Net的Forms認證,在整個開發和測試過程中沒有發現任何客戶登錄異常,但是發布后斷斷續續有用戶反映在登錄頁面登錄成功后跳轉主頁后,主頁並沒有識別登錄用戶,也即是Form 認證失敗。Asp.Net的Form認證大家應該有所了解,其內部的機制就是把用戶數據加密后保存在一個基於cookie的票據FormsAuthenticationTicket中,即認證過程中要借助於cookie。初步判斷問題出在cookie上,以下是問題的調查過程。
搭建調試環境
移動平台web開發中,調試不像桌面web開發中那么容易,難點在於移動設備多種多樣,並且移動設備中也很少有幫助調試的工具。為了測試如上的問題,我們借助了一個強大的HTTP監控工具Fiddler。 Fiddler可以設置容許遠程設備通過代理訪問服務器,這樣Fiddler就可以監控移動設備中的HTTP請求。Fddler中的設置如下圖:
設置移動設備的網絡訪問代理為Fiddler所在的機器IP,端口為如上圖所示Fiddler設置監控的端口號,這樣就可以監控移動的所有HTTP的請求了。
確定問題點
搭建好測試環境后,就開始確認問題了,用戶無法登陸,那么是否cookie丟失了呢? 認證的cookie是在登錄畫面設置的,登錄畫面的請求是通過HTTPS協議的,而首頁的請求是通過HTTP協議的,通過查看Fiddler,用戶登錄后,登錄頁面成功設置了認證cookie,並且首頁請求時也把認證cookie發回到了服務器端,監控信息如下圖顯示:
登錄畫面設置的cookie
首頁發回到服務器端的cookie
至此可以判斷,問題不是在客戶端,而是在服務器端,那么為什么服務器端不能識別返回的認證信息呢?為什么有些從某些設備上登錄就失敗呢?這些設備的瀏覽器發到服務器端的請求唯一的差別就是user-agent,那么服務器端針對user-agent又做了些什么呢?帶着這些問題,又深入地研究了一下ASP.NET認證原理,以及ASP.NET中有關cookie方面的一些特性。
深入理解cookieless在ASP.NET Form認證中的應用
要確認服務器端為什么沒有成功認證,那么必須要理解ASP.Net中的cookieless功能。
cookie失效有很多原因,有用戶禁用cookie的,也有設備不支持cookie的,所以ASP.NET中加入了cookieless這樣一個特性,使得當cookie失效是也能提供一種類似cookie的作用,比如可以把信息通過URL傳遞,cookieless這個功能就是基於這樣的目的而加入的。
cookieless總共有四種模式:
1,”UseCookies”,即cookieless功能不啟用
2,”UseUri”,即cookieless功能對所有設備啟用
3,”UseDeviceProfile”,根據發起請求的瀏覽器來確定應用或者不應用Cookieless,如果ASP.NET識別瀏覽器不能夠支持cookies,那么就啟用cookieless功能。另外從技術上說,如果瀏覽器支持cookie,那么如下兩個屬性的值為true:Request.Browser.Cookies 和 Request.Browser.SupportsRedirectWithCookie。
4, “AutoDetect”,從直接意思上理解,就是由ASP.NET來檢測當前瀏覽器是否支持cookies,這個模式有些迷惑用戶,並且也較復雜,官方文檔上有解釋這種模式的偽代碼
以上模式中UseDebiceProfile和AutoDetect模式依賴於設備,ASP.NET在維護一份數據庫,這份數據庫一般保存在如下的路徑中:%WINDIR%\Microsoft.Net\Framework\v2.0.50727\CONFIG\Browsers,數據庫中保存有已知的各種設備的兼容性,比如是否支持cookie,支持那些版本的javascript等等,從各種設備上的瀏覽器中發到服務器端的請求都會在HTTP頭中帶上特有的能標識設備的user-agent,ASP.NET就會自動根據user-agent到數據庫中比對,然后確定設備的兼容性。
這個cookieless功能也應用到了ASP.NET的Form認證中了,在web.config中的認證設置中可以配置cookieless屬性。默認情況下,Form 認證系統會根據發起請求的user agent來決定認證票是保存在cookie中還是包含在URL中,已知主流的桌面瀏覽器都是支持cookie的,但並不是所有移動版的瀏覽器都支持cookie。再次回到本篇文章所調查的bug中來,有些用戶不能正常登錄就是因為這些用戶所使用的設備被ASP.NET識別為不能支持cookie所致的,盡管設備本身是支持cookie的,比如我自己的手持設備MOTO Droid X,從這台設備的默認瀏覽器發出的請求中包含的user-agent為:User-Agent: Mozilla/5.0 (Linux; U; Android 2.3.3; zh-cn; DROIDX Build/4.5.1_57_DX5-18) AppleWebKit/533.1 (KHTML, like Gecko) H,/4.0 ��O�/533.1,經過ASP.NET識別為不支持cookie,所以這台設備無法正常登錄我們的web App。
以上我們調查清楚了登錄失敗的原因了,下面是給出具體的解決方案。
解決方案
明白了以上的Form認證原理,那么我們很容易想到,這是因為移動設備的user-agent無法被系統正確地識別而導致的cookie被禁用,這里有兩個解決方案:
方案1,覆蓋系統配置,讓所有的設備都能被系統識別為支持cookie。
Asp.NET提供了一種機制,讓我們可以自定義某些特定設備的系統支持,在工程中添加系統文件夾Asp_Browsers,並且添加自定義配置文件,如下是為了解決如上問題而添加的配置文件。
配置內容如下:
<browser refID="Mozilla" > <capabilities> <capability name="cookies" value="true" /> </capabilities> </browser> <browser refID="Default"> <capabilities> <capability name="cookies" value="true" /> </capabilities> </browser>
如上的配置代碼表示,對所有的設備,都是支持cookies的,這樣就解決了某些設備上的cookies問題了。
方案2,更改form的默認設置,讓系統不再根據設備來判斷是否支持cookie
在站點的配置文件中有關於Form認證的配置,在配置<authentication mode="Forms">/<forms>中要加上cookieless="UseCookies",默認的配置是UseDeviceProfile,即工具設備來決定Cookies的支持。
以上兩種方案的目的是一樣的,即讓系統認為所有的設備都支持cookie,第一種更靈活,第二種解決問題更徹底,可以根據實際情況選擇合適的方案。
后記
微軟的cookieless設計本身是不錯,但是這個默認值為UseDeviceProfile是值得商榷的,如今的瀏覽器百花齊放,每個瀏覽器都有其特有的user-agent,尤其是移動版的瀏覽器,即使是相同的瀏覽器,不同的設備制造商也會在user-agent加上標識設備型號和品牌的信息,微軟又不可能頻繁更新ASP.NET維護的瀏覽器配置,所以就會出現大量的誤判情況。以目前的狀況,系統應該把默認值設置為UseCookies,即默認為所有的瀏覽器瀏覽器支持cookie。
參考文檔: