對於Web應用的表單身分識別驗證,因為公司有一個類庫,採用 Session 實現,所以一直都沒有去仔細瞭解。其實我並不贊成在 .NET 中用 Session 實現身分識別驗證,畢竟 .NET 提供了一個強大的身分識別驗證體系,並且公司的類庫也沒有實現什麼特殊的功能,僅只是儲存一個 Session 變數來提供身份識別,在安全性和可用性上與 .NET 的實現相比,個人感覺還是有較大的差距。 近期很少加班,就抽空看了一下,理了個大致的思路出來。 首先,自然是配置 Web.config,在 <system.web> 下設定: <authentication mode="Forms"> <forms name=".SomeTsteAuth" loginUrl="admin/login.aspx" defaultUrl="admin/index.aspx" path="/" timeout="10"> </forms> </authentication> <authentication> 的 Mode=”Forms” 指定 Web 應用程式採用表單驗證,另外的方式還有“Windows”、“Passport”和“None”,“Windows”常用在區域網路中,配合 AD 進行身分識別驗證,“Passport”好像要交錢給微軟後才能夠使用,不太清楚了。“None”表示不進行驗證。 <forms> 的幾個常用屬性: name 屬性指定驗證所需要的 cookie 的名稱,預設值是“.ASPXAUTH”,如果在一個伺服器上下掛了多個 Web 應用程式,必須重新指定該名稱,因為每個應用程式都需要唯一的 cookie。 loginUrl 屬性指定登入用的頁面,用於提供使用者名稱和密碼,預設值是“login.aspx”。該頁面可以和需要提供身分識別驗證才能訪問的頁面放在同一個目錄下(呵,我原以為這個頁面要放在單獨的可公開訪問的目錄下)。 defaultUrl 屬性指定登入後跳轉到的頁面,預設值是“default.aspx”,當然你也可以跳轉到使用者登入前的前一個頁面,並且這是 .NET 的預設實現。 path 屬性指定 cookie 的路徑,預設值為“/”,對於大多數瀏覽器而言,cookie 的 path 是區分大小寫,因此如果路徑的大小寫不符,將不會發回 cookie。(注意:“/”指網站的根目錄,在開發時,Visual studio 通常會在網站根目錄下建立一個新的目錄作為 Web 應用程式的根,例如http://localhost/MySite,此時若你要單獨針對 Web 應用程式設定 path,必須從網站根目錄後的路徑指定,例如,要設定剛才的 Web 應用程式訪問 Admin 目錄下的檔案需要身分識別驗證時,path 值應為“MySite/Admin”,而不是“Admin”,否則使用者將無法正常登入。) timeout 屬性指定使用者多長時間不進行操作,身份憑證會到期,以分鐘為單位,預設為 30 分鐘。 設定好 <authentication> 以後,還需要設定 <authorication>,最常見的方式如下: <authorization> <deny users="?"/> </authorization> “?”號表示匿名使用者,“*”號表示所有使用者。在網上看到一些文檔,在<deny users=”?” /> 下面還有一句 <allow users=”*” />,其實沒必要增加這一句,除非使用角色對各個子目錄分別進行許可權控制。因為這樣有一個潛在的危險,如果有人不小心把 <allow users=”*” /> 放在了 <deny user=”?” /> 前面,系統將不會進行驗證。 如果要對子目錄進行分別的許可權管控,需要新增一個 <location> 段到 Web.config 的根項目 <configuration> 下: <location path="admin"> <system.web> <authorization> <deny users="?"/> </authorization> </system.web> </location> 如果涉及到多個子目錄,並要分配不同的許可權,那就需要使用角色。將 <authorization> 下的內容換成 <allow roles="Admin"/> <deny users="*"/> Web.config 的配置大致如此,需要注意的一點是 <authentication> 節一個 Web 應用程式只能有一個,即我們在一個 Web 應用程式中只能採用一種驗證方式,而 <location> 節可以配置多個,以對各目錄進行不同的許可權管控。對於未配置 <authorization> 節的目錄,存取權限與 Web 應用程式根相同,若應用程式的根也未配置 <authorization>,則預設為任何人可訪問,即使 <authentication> 的 mode 屬性設定為“Forms”。 其次就是編碼,在登入頁面的“登入”按鈕點擊事件中: if (UserName.Text.Trim() == "你的使用者名稱" && Password.Text == "你的密碼") FormsAuthentication.RedirectFromLoginPage("你的使用者名稱", false); else //提示使用者名稱和密碼不正確 RedirectFromLoginPage() 的第一個參數是目前正在驗證的使用者名稱,第二個參數指是否長期儲存登入資訊到 cookie 中,這個參數的意義在 .NET 2.0 中與 .NET 1.1 中不同,在 .NET 1.1 中,會將登入資訊儲存到 cookie 中,並設定到期時間為 50 年後,即你以後再也不用輸入使用者名稱和密碼,除非 cookie 被刪除或 50 年後(老眼昏花的你加上一台堪稱古董的電腦,最美不過夕陽紅)。在 .NET 2.0 中,這個參數僅指在關閉瀏覽器後,登入資訊在 cookie 中是否還存在,而到期時間的約束依然有效,即 cookie 到期後,無論你重啟瀏覽器與否,仍需輸入登入憑證。 代碼簡單得出乎意料,.NET 會自動建立票劵並重新導向到登入前使用者訪問的那個需要進行驗證的頁面,若使用者直接存取登入頁面,則重新導向到 Web.config 中定義的 default 頁面。如果需要自己控制重新導向的過程,可以這麼做: if (UserName.Text.Trim() == "你的使用者名稱" && Password.Text == "你的密碼") { FormsAuthentication.SetAuthCookie("你的使用者名稱", false); Response.Redirect("Index.aspx"); } else //提示使用者名稱和密碼不正確 其實,語句 FormsAuthentication.RedirectFromLoginPage("你的使用者名稱", false) 等同於 FormsAuthentication.SetAuthCookie("你的使用者名稱", false); Response.Redirect(FormsAuthentication.GetRedirectUrl("你的使用者名稱", false)); 當然,也可以手動建立票劵,並加入到響應的 cookie 集合中,完整的代碼如下: if (UserName.Text.Trim() == "你的使用者名稱" && Password.Text == "你的密碼") { //為當前登入使用者建立一個新的票劵 FormsAuthenticationTicket ticket = new FormsAuthenticationTicket( 2, //版本號碼 "你的使用者名稱", //登入的使用者名稱 DateTime.Now, //票劵發布時間 DateTime.Now.AddMinutes(15), //票劵到期時間 false, //是否在關閉瀏覽器後仍然保留登入資訊 "", //可加入少量使用者資料(注意:不能為 null) FormsAuthentication.FormsCookiePath //cookie 路徑 ); //加密票劵,擷取加密後的字串 string encrypt = FormsAuthentication.Encrypt(ticket); //使用加密後的字串建立一個 cookie HttpCookie cookie = new HttpCookie( FormsAuthentication.FormsCookieName, Encrypt ); //將 cookie 增加到用戶端 Response.Cookies.Add(cookie); Response.Redirect(FormsAuthentication.GetRedirectUrl("你的使用者名稱", false)); } else //提示使用者名稱和密碼不正確 到這裡,應該很清楚了,表單驗證,其實質是使用一個特定的 cookie,在每次串連伺服器時驗證該 cookie 是否存在,從而決定使用者是否具有相應的許可權。在上述代碼中,也可以增加對 cookie 控制的代碼,在使用加密後的票劵建立一個 cookie 後,增加代碼: //HttpOnly 屬性為 true,表示該 cookie 不能在瀏覽器端進行存取 cookie.HttpOnly = true; //cookie 的路徑,取 Web.config 中 <forms> 屬性 path 的值 cookie.Path = FormsAuthentication.FormsCookiePath; //設定 cookie 的到期時間,與票劵的到期時間一致,如果這兩個時間不一致,則其中任何一個時間到期時,均視為到期 cookie.Expires = ticket.Expiration; 清楚表單驗證的大致機制後,對於基於角色的表單驗證也是手到擒來,大致的過程敘述如下: 1 在登入頁面的代碼檔案中,新增一個普通的 cookie,將使用者所屬的角色儲存到該 cookie 中(一個使用者可具備多個角色)。 2 在 Global.asax 的 AuthenticateRequest 事件中,判斷使用者是否已通過驗證,若已通過驗證,則從1增加的 cookie 中取出角色字串,並構建一個 System.Security.Principal.GenericPrincipal 對象,該對象的建構函式包括兩個參數:使用者標識和角色數組,使用者標識可通過 HttpContext.Current.User.Identity 取得,角色數組將角色字串轉換為字串數組賦進去即可。 3 調整 Web.config 設定角色的許可權。 |