適用于:
ASP.NET 2.0 版
摘要:本教程闡釋 ASP.NET 2.0 版中窗體身份驗證的工作機制;闡釋 IIS 和 ASP.NET 身份驗證如何協(xié)作,以及 FormsAuthenticationModule 類的角色與操作。
目標
• |
了解 ASP.NET 2.0 版中窗體身份驗證的工作機制。 |
• |
了解窗體身份驗證配置如何影響窗體身份驗證票的生成。 |
• |
查看窗體身份驗證票中存儲了什么內(nèi)容。 |
• |
了解 cookieless 窗體身份驗證的工作機制。 |
• |
了解窗體身份驗證的 Web 場 (Web farm) 注意事項。 |
概述
窗體身份驗證使用用戶登錄到站點時創(chuàng)建的身份驗證票,然后在整個站點內(nèi)跟蹤該用戶。窗體身份驗證票通常包含在一個 Cookie 中。然而,ASP.NET 2.0 版支持無 Cookie 窗體身份驗證,結(jié)果是將票證傳入查詢字符串中。
如果用戶請求一個需要經(jīng)過身份驗證的訪問的頁,且該用戶以前沒有登錄過該站點,則該用戶重定向到一個配置好的登錄頁。該登錄頁提示用戶提供憑據(jù)(通常是用戶名和密碼)。然后,將這些憑據(jù)傳遞給服務器并針對用戶存儲(如 SQL Server 數(shù)據(jù)庫)進行驗證。在 ASP.NET 2.0 中,用戶存儲訪問可由成員身份提供程序處理。對用戶的憑據(jù)進行身份驗證后,用戶重定向到原來請求的頁面。
窗體身份驗證處理由 FormsAuthenticationModule 類實現(xiàn),該類是一個參與常規(guī) ASP.NET 頁處理循環(huán)的 HTTP 模塊。本文闡釋 ASP.NET 2.0 中窗體身份驗證的工作機制。
IIS 身份驗證
ASP.NET 身份驗證分為兩個步驟。首先,Internet 信息服務 (IIS) 對用戶進行身份驗證,并創(chuàng)建一個 Windows 令牌來表示該用戶。IIS 通過查看 IIS 元數(shù)據(jù)庫設置,確定應該對特定應用程序使用的身份驗證模式。如果 IIS 配置為使用匿名身份驗證,則為 IUSR_MACHINE 帳戶生成一個令牌并用它表示匿名用戶。然后,IIS 將該令牌傳遞給 ASP.NET。
其次,ASP.NET 執(zhí)行自己的身份驗證。所使用的身份驗證方法由 authentication 元素的 mode 屬性指定。以下身份驗證配置指定 ASP.NET 使用 FormsAuthenticationModule 類:
<authentication mode="Forms" />
注 由于窗體身份驗證不依賴于 IIS 身份驗證,因此如果要在 ASP.NET 應用程序中使用窗體身份驗證,則應該在 IIS 中為應用程序配置匿名訪問。
ASP.NET 窗體身份驗證
ASP.NET 窗體身份驗證在 IIS 身份驗證完成后發(fā)生。可以使用 forms 元素配置窗體身份驗證。
窗體身份驗證配置
以下配置文件片段顯示窗體身份驗證的默認屬性值。
<system.web> <authentication mode="Forms"> <forms loginUrl="Login.aspx" protection="All" timeout="30" name=".ASPXAUTH" path="/" requireSSL="false" slidingExpiration="true" defaultUrl="default.aspx" cookieless="UseDeviceProfile" enableCrossAppRedirects="false" /> </authentication> </system.web>
下面是對默認屬性值的描述:
• |
loginUrl 指向應用程序的自定義登錄頁。應該將登錄頁放在需要安全套接字層 (SSL) 的文件夾中。這有助于確保憑據(jù)從瀏覽器傳到 Web 服務器時的完整性。 |
• |
protection 設置為 All,以指定窗體身份驗證票的保密性和完整性。這導致使用 machineKey 元素上指定的算法對身份驗證票證進行加密,并且使用同樣是 machineKey 元素上指定的哈希算法進行簽名。 |
• |
timeout 用于指定窗體身份驗證會話的有限生存期。默認值為 30 分鐘。如果頒發(fā)持久的窗體身份驗證 Cookie,timeout 屬性還用于設置持久 Cookie 的生存期。 |
• |
name 和 path 設置為應用程序的配置文件中定義的值。 |
• |
requireSSL 設置為 false。該配置意味著身份驗證 Cookie 可通過未經(jīng) SSL 加密的信道進行傳輸。如果擔心會話竊取,應考慮將 requireSSL 設置為 true。 |
• |
slidingExpiration 設置為 true 以執(zhí)行變化的會話生存期。這意味著只要用戶在站點上處于活動狀態(tài),會話超時就會定期重置。 |
• |
defaultUrl 設置為應用程序的 Default.aspx 頁。 |
• |
cookieless 設置為 UseDeviceProfile,以指定應用程序?qū)λ兄С?Cookie 的瀏覽器都使用 Cookie。如果不支持 Cookie 的瀏覽器訪問該站點,窗體身份驗證在 URL 上打包身份驗證票。 |
• |
enableCrossAppRedirects 設置為 false,以指明窗體身份驗證不支持自動處理在應用程序之間傳遞的查詢字符串上的票證以及作為某個窗體 POST 的一部分傳遞的票證。 |
授權(quán)配置
在 IIS 中,對所有使用窗體身份驗證的應用程序啟用異步訪問。UrlAuthorizationModule 類用于幫助確保只有經(jīng)過身份驗證的用戶才能訪問頁。
可以使用 authorization 元素配置 UrlAuthorizationModule,如以下示例所示。
<system.web> <authorization> <deny users="?" /> </authorization> </system.web>
使用該設置將拒絕所有未經(jīng)過身份驗證的用戶訪問應用程序中的任何頁。如果未經(jīng)身份驗證的用戶試圖訪問某頁,窗體身份驗證模塊將該用戶重定向到 forms 元素的 loginUrl 屬性指定的登錄頁。
窗體身份驗證控制流
圖 1 顯示窗體身份驗證期間出現(xiàn)的事件順序。
圖 1. 窗體身份驗證控制流
• |
用戶請求應用程序的虛擬目錄下的 Default.aspx 文件。因為 IIS 元數(shù)據(jù)庫中啟用了匿名訪問,因此 IIS 允許該請求。ASP.NET 確認 authorization 元素包括 <deny users="?" /> 標記。 |
• |
服務器查找一個身份驗證 Cookie。如果找不到該身份驗證 Cookie,則用戶重定向到配置好的登錄頁 (Login.aspx),該頁由 forms 元素的 LoginUrl 屬性。用戶通過該窗體提供和提交憑據(jù)。有關(guān)起始頁的信息存放在使用 RETURNURL 作為密鑰的查詢字符串中。服務器 HTTP 應答如下所示: 302 Found Location: http://localhost/FormsAuthTest/login.aspx?RETURNURL=%2fFormAuthTest%2fDefault.aspx |
• |
瀏覽器請求 Login.aspx 頁,并在查詢字符串中包括 RETURNURL 參數(shù)。 |
• |
服務器返回登錄頁以及 200 OK HTTP 狀態(tài)代碼。 |
• |
用戶在登錄頁輸入憑據(jù),并將該頁(包括來自查詢字符串的 RETURNURL 參數(shù))發(fā)送回服務器。 |
• |
服務器根據(jù)某個存儲(如 SQL Server 數(shù)據(jù)庫或 Active Directory 用戶存儲)驗證用戶憑據(jù)。登錄頁中的代碼創(chuàng)建一個包含為該會話設置的窗體身份驗證票的 Cookie。 在 ASP.NET 2.0 中,可以通過成員身份系統(tǒng)執(zhí)行對用戶憑據(jù)的驗證。Membership 類為此提供了 ValidateUser 方法,如下所示: if (Membership.ValidateUser(userName.Text, password.Text)) { if (Request.QueryString["ReturnUrl"] != null) { FormsAuthentication.RedirectFromLoginPage(userName.Text, false); } else { FormsAuthentication.SetAuthCookie(userName.Text, false); } } else { Response.Write("Invalid UserID and Password"); } 注 使用 Login Web 服務器控件時,它自動為您執(zhí)行以下步驟。下文使用了前面提供的代碼。 |
• |
對于經(jīng)過身份驗證的用戶,服務器將瀏覽器重定向到查詢字符串中的 RETURNURL 參數(shù)指定的原始 URL。服務器 HTTP 應答如下所示: 302 Found Location: http://localhost/TestSample/default.aspx |
• |
重定向之后,瀏覽器再次請求 Default.aspx 頁。該請求包括身份驗證 Cookie。 |
• |
FormsAuthenticationModule 類檢測窗體身份驗證 Cookie 并對用戶進行身份驗證。身份驗證成功后,FormsAuthenticationModule 類使用有關(guān)經(jīng)過身份驗證的用戶的信息填充當前的 User 屬性(由 HttpContext 對象公開)。 |
• |
由于服務器已經(jīng)驗證了身份驗證 Cookie,因此它允許訪問并返回 Default.aspx 頁。 |
FormsAuthenticationModule
ASP.NET 2.0 在計算機級 Web.config 文件中定義了一組 HTTP 模塊,包括大量身份驗證模塊,如下所示:
<httpModules> ... <add name="WindowsAuthentication" type="System.Web.Security.WindowsAuthenticationModule" /> <add name="FormsAuthentication" type="System.Web.Security.FormsAuthenticationModule" /> <add name="PassportAuthentication" type="System.Web.Security.PassportAuthenticationModule" /> ... </httpModules>
每個請求只能使用一個身份驗證模塊。所使用的身份驗證模塊取決于 authentication 元素(通常位于應用程序的虛擬目錄中的 Web.config 文件中)指定了哪種身份驗證模式。
當 Web.config 文件中包含以下元素時,激活 FormsAuthenticationModule 類。
<authentication mode="Forms" />
FormsAuthenticationModule 類構(gòu)造一個 GenericPrincipal 對象并將其存儲在 HTTP 上下文中。GenericPrincipal 對象保存對一個 FormsIdentity 實例的引用,該實例代表當前經(jīng)過身份驗證的用戶。應該允許窗體身份驗證為您管理這些任務。如果應用程序有特定要求(例如,將 User 屬性設置為一個實現(xiàn) IPrincipal 接口的自定義類),則該應用程序應該處理 PostAuthenticate 事件。FormsAuthenticationModule 驗證了窗體身份驗證 Cookie 并創(chuàng)建了 GenericPrincipal 和 FormsIdentity 對象之后,會發(fā)生 PostAuthenticate 事件。在該代碼中,可以構(gòu)造一個包裝 FormsIdentity 對象的自定義 IPrincipal 對象,然后將它存儲在 HttpContext. User 屬性中。
注 如果執(zhí)行了這一操作,還需要設置 Thread.CurrentPrincipal 屬性上的 IPrincipal 引用,以確保 HttpContext 對象和該線程指向相同的身份驗證信息。
窗體身份驗證 Cookie
調(diào)用 FormsAuthentication.SetAuthCookie 或FormsAuthentication.RedirectFromLoginPage 方法時,FormsAuthentication 類自動創(chuàng)建身份驗證 Cookie。
典型的窗體身份驗證 Cookie 中包括以下屬性:
• |
Name。該屬性指定 Cookie 的名稱。 | ||
• |
Value。該屬性指定 Cookie 的值。 在典型的窗體身份驗證 Cookie 中,該值包含一個經(jīng)過加密和簽名的 FormsAuthenticationTicket 對象的字符串表示形式。該 Cookie 包含以下屬性: | ||
• |
Expires。該屬性指定 Cookie 的到期日期和時間。僅當代碼指示應該頒發(fā)一個持久的窗體身份驗證 Cookie,窗體身份驗證才設置該值。 | ||
• |
Domain。該屬性指定與 Cookie 關(guān)聯(lián)的域。默認值為 null。
| ||
• |
HttpOnly。該屬性指定是否可以通過客戶端腳本訪問該 Cookie。在 ASP.NET 2.0 中,該值始終設置為 true。Internet Explorer 6 Service Pack 1 支持該 Cookie 屬性,從而防止客戶端腳本從 document.cookie 屬性訪問該 Cookie。如果嘗試從客戶端腳本訪問該 Cookie,則返回一個空字符串。無論何時用戶瀏覽到當前域中的 Web 站點,該 Cookie 仍然發(fā)送至服務器。 注 不支持 HttpOnly Cookie 屬性的 Web 瀏覽器要么忽略該 Cookie,要么忽略該屬性,這意味著會話仍然容易受到跨站點腳本的攻擊。 | ||
• |
Path。該屬性指定 Cookie 的虛擬路徑。默認值為"/",代表根目錄。 | ||
• |
Secure。該屬性指出 Cookie 是否應該僅通過 HTTPS 連接傳輸。Secure 屬性應設置為 true,以便該 Cookie 可以受 SSL 加密的保護。 | ||
• |
Version。該屬性指定 Cookie 的版本號。 |
創(chuàng)建身份驗證 Cookie
通過 FormsAuthentication 類創(chuàng)建身份驗證 Cookie,如下所示。用戶經(jīng)過驗證后,FormsAuthentication 類在內(nèi)部創(chuàng)建一個 FormsAuthenticationTicket 對象,方法是指定 Cookie 名、Cookie 版本、目錄路徑、Cookie 頒發(fā)日期;Cookie 到期日期、是否應該保留 Cookie,以及用戶定義的數(shù)據(jù)(可選)。
FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(1, "userName", DateTime.Now, DateTime.Now.AddMinutes(30), // value of time out property false, // Value of IsPersistent property String.Empty, FormsAuthentication.FormsCookiePath);
接下來,如果 forms 元素的 protection 屬性設置為 All 或 Encryption,則窗體身份驗證使用 Encrypt 方法對窗體身份驗證票進行加密和簽名。
string encryptedTicket = FormsAuthentication.Encrypt(ticket);
以下文本顯示了當 protection 屬性設置為 All 時使用的過程:
• |
創(chuàng)建序列化窗體身份驗證票。創(chuàng)建票證的字節(jié)數(shù)組表示形式。 |
• |
對窗體身份驗證票進行簽名。字節(jié)數(shù)組的消息身份驗證代碼 (MAC) 的值,由使用 machineKey 元素的 validation 和 validationKey 屬性指定的算法和密鑰進行計算。默認情況下,使用 SHA1 算法。 |
• |
對窗體身份驗證票進行加密。已經(jīng)創(chuàng)建的第二個字節(jié)數(shù)組使用 FormsAuthentication 類的 Encrypt 方法進行加密。該 Encrypt 方法在內(nèi)部使用由 machineKey 元素上的 decryption 和 decryptionKey 屬性指定的算法和密鑰。ASP.NET 1.1 版在默認情況下使用 3DES 算法。ASP.NET 2.0 版在默認情況下使用 Rinjdael (AES) 算法。 |
• |
根據(jù)需要創(chuàng)建 HTTP Cookie 或查詢字符串。然后,如果窗體身份驗證針對 cookieless 身份驗證進行了配置,則加密的身份驗證票添加到 HttpCookie 對象。使用以下代碼創(chuàng)建該 Cookie 對象: HttpCookie authCookie = new HttpCookie( FormsAuthentication.FormsCookieName, encryptedTicket); |
• |
將窗體身份驗證Cookie 設置為安全的。如果窗體身份驗證票配置為使用 SSL,則 HttpCookie.Secure 屬性設置為 true。這表明瀏覽器僅通過 HTTPS 連接發(fā)送 Cookie。 authCookie.Secure = true; |
• |
設置 HttpOnly 位。在 ASP.NET 2.0 中,始終設置該位。 |
• |
設置適當?shù)?/B> Cookie 屬性。如果需要,設置 Cookie 的 path、domain 和 expires 屬性。 |
• |
將 Cookie 添加到 Cookie 集合。將身份驗證 Cookie 添加到要返回給客戶端瀏覽器的 Cookie 集合。 Response.Cookies.Add(authCookie); |
每次在身份驗證之后接收一個后續(xù)請求時,FormsAuthenticationModule 類都會從身份驗證 Cookie 中檢索身份驗證票,對其進行解密,計算哈希值,并比較該 MAC 值,以幫助確保該 Cookie 未被篡改。最后,驗證該窗體身份驗證票中包含的到期時間。
注 ASP.NET 并不依賴于 Cookie 的到期日期,因為該時間很容易偽造。
角色授權(quán)
在 ASP.NET 2.0 中,角色授權(quán)已經(jīng)得到簡化。對用戶進行身份驗證或者將角色細節(jié)添加到身份驗證 Cookie 時,不再需要檢索角色信息。.NET Framework 2.0 包括一個角色管理 API,它使您能夠創(chuàng)建和刪除角色,將用戶添加到角色以及從角色刪除用戶。該角色管理 API 將其數(shù)據(jù)存儲在一個基礎(chǔ)數(shù)據(jù)存儲中,它通過針對該數(shù)據(jù)存儲的適當角色提供程序訪問該存儲。以下角色提供程序為 .NET Framework 2.0 附帶,可以與窗體身份驗證一起使用:
• |
SQL Server。它是默認的提供程序,將角色信息存儲在 SQL Server 數(shù)據(jù)庫。 |
• |
授權(quán)管理器 (AzMan)。該提供程序使用 XML 文件、Active Directory 或 Active Directory 應用程序模式 (ADAM) 中的一個 AzMan 策略存儲作為其角色存儲。它通常用于 Intranet 或 Extranet 方案中,其中 Windows 身份驗證和 Active Directory 用于進行身份驗證。 |
有關(guān)如何使用角色管理 API 的詳細信息,請參閱 How To: Use Role Manager in ASP.NET 2.0。
Cookieless 窗體身份驗證
ASP.NET 2.0 支持 cookieless 窗體身份驗證。該功能由 forms 元素的 cookieless 屬性控制。該屬性可以設置為以下四個值之一:
• |
UseCookies。該值強制 FormsAuthenticationModule 類使用 Cookie 傳輸身份驗證票。 |
• |
UseUri。該值指示 FormsAuthenticationModule 類重寫 URL 來傳輸身份驗證票。 |
• |
UseDeviceProfile。該值指示 FormsAuthenticationModule 類查看瀏覽器功能。如果瀏覽器支持 Cookie,則使用 Cookie;否則,重寫 URL。 |
• |
AutoDetect。該值通過一個動態(tài)檢測機制指示 FormsAuthenticationModule 類檢測瀏覽器是否支持 Cookie。如果檢測邏輯表明不支持 Cookie,則重寫 URL。 |
如果應用程序配置為使用 cookieless 窗體身份驗證,并且正在使用 FormsAuthentication.RedirectFromLoginPage 方法,則 FormsAuthenticationModule 類自動設置 URL 中的窗體身份驗證票。以下代碼示例顯示了典型 URL 在重寫后的外觀:
http://localhost/CookielessFormsAuthTest/(F(-k9DcsrIY4CAW81Rbju8KRnJ5o_gOQe0I1E_jNJLYm74izyOJK8GWdfoebgePJTEws0Pci7fHgTOUFTJe9jvgA2))/Test.aspx
括號中的 URL 部分包含 Cookie 通常將包含的數(shù)據(jù)。該數(shù)據(jù)在請求處理過程中由 ASP.NET 刪除。該步驟由 ASP.NET ISAPI 篩選器執(zhí)行,而不是在 HttpModule 類中執(zhí)行。如果從一個 .aspx 頁讀取 Request.Path 屬性,您在 URL 中不會看到任何額外的信息。如果重定向請求,URL 將自動重寫。
注 難以保證 URL 中包含的身份驗證票的安全。當安全性極為重要時,您應該使用 Cookie 存儲身份驗證票。
成員身份和登錄控件
ASP.NET 2.0 引入了成員身份功能和一組登錄 Web 服務器控件,它們簡化了使用窗體身份驗證的應用程序的實現(xiàn)。
成員身份為應用程序用戶提供憑據(jù)存儲和管理。它還提供一個成員身份 API,可以在使用窗體身份驗證時簡化用戶憑據(jù)的驗證任務。該成員身份功能構(gòu)建于提供程序模型之上。該模型允許實現(xiàn)和配置指向不同用戶存儲的不同提供程序。ASP.NET 2.0 包括以下成員關(guān)系提供程序:
• |
Active Directory 成員關(guān)系提供程序。該提供程序使用 Active Directory 或 Active Directory 應用程序模式 (ADAM) 用戶存儲。 |
• |
SQL Server 成員關(guān)系提供程序。該提供程序使用 SQL Server 用戶存儲。 |
還可以添加對自定義用戶存儲的支持。例如,可以添加對其他輕量級目錄訪問協(xié)議 (LDAP) 目錄或其他現(xiàn)有公共標識存儲的支持。為此,創(chuàng)建一個從 MembershipProvider 抽象基類繼承的自定義提供程序。
ASP.NET 登錄控件自動使用成員身份和窗體身份驗證,并封裝提示用戶輸入憑據(jù),驗證用戶,恢復或替換密碼等所需的邏輯。實際上,ASP.NET 登錄控件在窗體身份驗證和成員身份上提供一個抽象層,并且取代了您使用窗體身份驗證時通常必須進行的大多數(shù)或全部工作。
有關(guān)使用成員身份功能和登錄控件的詳細信息,請參閱 How To: Use Membership in ASP.NET 2.0。
Web 場方案
在 Web 場中,無法確保哪個服務器將處理連續(xù)請求。如果用戶在一臺服務器上經(jīng)過身份驗證,但下一個請求在另一臺服務器上進行,則身份驗證票將導致驗證失敗并請求用戶重新進行身份驗證。
machineKey 元素中的 validationKey 和 decryptionKey 屬性用于對窗體身份驗證票進行哈希操作和加密。這些屬性的默認值為 AutoGenerate.IsolateApps。這些密鑰是針對每個應用程序自動生成的,在每臺服務器上都不同。因此,在一臺計算機上加密的身份驗證票無法在 Web 場中的另一臺計算機或者同一臺 Web 服務器上的另一個應用程序中進行解密和驗證。
為了解決該問題, Web 場中所有計算機上的 validationKey 和 decryptionKey 值都必須相同。有關(guān)配置 machineKey 元素的詳細信息,請參閱 How To: Configure MachineKey in ASP.NET 2.0。
其他資源
反饋
使用 Wiki 或電子郵件提供反饋:
• |
Wiki。Security Guidance Feedback Wiki 頁: http://channel9.msdn.com/wiki/default.aspx/Channel9.SecurityGuidanceFeedback |
• |
電子郵件。請將電子郵件發(fā)送至 secguide@Microsoft.com。 |
我們對有關(guān)以下內(nèi)容的反饋特別感興趣:
• |
特定于建議的技術(shù)問題 |
• |
有用性和可用性問題 |
技術(shù)支持
本指南中涉及的 Microsoft 產(chǎn)品和技術(shù)的技術(shù)支持由 Microsoft Support Services 提供。有關(guān)產(chǎn)品支持信息,請訪問 Microsoft Support 的 Web 站點:http://support.Microsoft.com。
社區(qū)和新聞組
論壇和新聞組中提供社區(qū)支持:
• | |
• |
ASP.NET 論壇: http://forums.ASP.NET |
要想獲益最多,請找到與您的技術(shù)或問題對應的新聞組。例如,如果有 ASP.NET 安全功能方面的問題,可查閱 ASP.NET Security 論壇。
投稿人與審閱者
• |
外部投稿人和審閱者: Jason Taylor, Security Innovation; Rudolph Araujo, Foundstone Professional Services |
• |
Microsoft PSS 投稿人和審閱者: Tom Christian, Wade Mascia |
• |
Microsoft 產(chǎn)品小組: Stefan Schackow |
• |
測試小組: Larry Brader,Microsoft Corporation;Nadupalli Venkata Surya Sateesh、Sivanthapatham Shanmugasundaram,Infosys Technologies Ltd. |
• |
編輯小組: Lara Ballinger、Nelly Delgado,Microsoft Corporation |
• |
發(fā)布管理: Sanjeev Garg,Microsoft Corporation |