使用 C#.NET 在 ASP.NET 應用程式中實作表單型驗證
本文示範如何使用資料庫來儲存使用者,來實作窗體型驗證。 它指的是下列Microsoft .NET Framework 類別庫命名空間:
System.Data.SqlClient
System.Web.Security
原始產品版本: ASP.NET
原始 KB 編號: 301240
需求
下列清單概述您需要的建議硬體、軟體、網路基礎結構和 Service Pack:
- Visual Studio .NET
- 網際網路資訊服務 (IIS) 5.0 版或更新版本
- SQL Server
使用 C# .NET 建立 ASP.NET 應用程式
- 開啟 Visual Studio .NET。
- 建立新的 ASP.NET Web 應用程式,並指定名稱和位置。
在 Web.config 檔案中設定安全性設定
本節示範如何新增和修改 <authentication>
和 <authorization>
組態區段,以設定 ASP.NET 應用程式以使用表單型驗證。
在 方案總管 中,開啟 Web.config 檔案。
將驗證模式變更為 Forms。
插入標記
<Forms>
,並填入適當的屬性。 複製下列程式代碼,然後選取 [編輯] 功能表上的 [貼上為 HTML],以在檔案的 區段中貼上程式代碼<authentication>
:<authentication mode="Forms"> <forms name=".ASPXFORMSDEMO" loginUrl="logon.aspx" protection="All" path="/" timeout="30" /> </authentication>
拒絕存取
<authorization>
區段中的匿名使用者,如下所示:<authorization> <deny users ="?" /> <allow users = "*" /> </authorization>
建立範例資料庫數據表以儲存使用者詳細數據
本節說明如何建立範例資料庫來儲存使用者的使用者名稱、密碼和角色。 如果您想要將使用者角色儲存在資料庫中,並實作角色型安全性,則需要角色數據行。
在 [ 開始] 功能表上,選取 [執行],然後輸入記事本以開啟 [記事本]。
反白顯示下列 SQL 腳本程式代碼,以滑鼠右鍵按兩下程式代碼,然後選取 [ 複製]。 在 [記事本] 中,選取 [編輯] 功能表上的 [貼上] 以貼上下列程序代碼:
if exists (select * from sysobjects where id = object_id(N'[dbo].[Users]') and OBJECTPROPERTY(id, N'IsUserTable') = 1) drop table [dbo].[Users] GO CREATE TABLE [dbo].[Users] ([uname] [varchar] (15) NOT NULL, [Pwd] [varchar] (25) NOT NULL, [userRole] [varchar] (25) NOT NULL, ) ON [PRIMARY] GO ALTER TABLE [dbo].[Users] WITH NOCHECK ADD CONSTRAINT [PK_Users] PRIMARY KEY NONCLUSTERED ([uname] ) ON [PRIMARY] GO INSERT INTO Users values('user1','user1','Manager') INSERT INTO Users values('user2','user2','Admin') INSERT INTO Users values('user3','user3','User') GO
將檔案儲存為 Users.sql。
在 SQL Server 計算機上,在查詢分析器中開啟 Users.sql 。 從資料庫清單中,選取 [發佈],然後執行腳本。 此作業會建立範例用戶數據表,並在 Pubs 資料庫中填入要與這個範例應用程式搭配使用的數據表。
建立Logon.aspx頁面
將新的 Web Form 新增至名為 Logon.aspx 的專案。
在編輯器中開啟Logon.aspx頁面,然後切換至 HTML 檢視。
複製下列程式代碼,並使用 [編輯] 功能表上的 [貼上為 HTML] 選項,在標記之間
<form>
插入程序代碼:<h3> <font face="Verdana">Logon Page</font> </h3> <table> <tr> <td>Email:</td> <td><input id="txtUserName" type="text" runat="server"></td> <td><ASP:RequiredFieldValidator ControlToValidate="txtUserName" Display="Static" ErrorMessage="*" runat="server" ID="vUserName" /></td> </tr> <tr> <td>Password:</td> <td><input id="txtUserPass" type="password" runat="server"></td> <td><ASP:RequiredFieldValidator ControlToValidate="txtUserPass" Display="Static" ErrorMessage="*" runat="server" ID="vUserPass" /> </td> </tr> <tr> <td>Persistent Cookie:</td> <td><ASP:CheckBox id="chkPersistCookie" runat="server" autopostback="false" /></td> <td></td> </tr> </table> <input type="submit" Value="Logon" runat="server" ID="cmdLogin"><p></p> <asp:Label id="lblMsg" ForeColor="red" Font-Name="Verdana" Font-Size="10" runat="server" />
此 Web 表單可用來向使用者呈現登入表單,以便他們提供使用者名稱和密碼來登入應用程式。
如需詳細資訊,請參閱 RequiredFieldValidator 類別。
切換至 [設計] 檢視,然後儲存頁面。
撰寫事件處理程式的程式代碼,使其驗證用戶認證
本節提供置於程序代碼後置頁面 (Logon.aspx.cs) 的程序代碼。
按兩下 [ 登入] 以開啟 Logon.aspx.cs 檔案。
在程式代碼後置檔案中匯入必要的命名空間:
using System.Data.SqlClient; using System.Web.Security;
藉由在資料庫中尋找來建立函
ValidateUser
式來驗證用戶認證。 請確定您將字串變更Connection
為指向資料庫。private bool ValidateUser( string userName, string passWord ) { SqlConnection conn; SqlCommand cmd; string lookupPassword = null; // Check for invalid userName. // userName must not be null and must be between 1 and 15 characters. if ( ( null == userName ) || ( 0 == userName.Length ) || ( userName.Length > 15 )) { System.Diagnostics.Trace.WriteLine( "[ValidateUser] Input validation of userName failed." ); return false; } // Check for invalid passWord. // passWord must not be null and must be between 1 and 25 characters. if ( ( null == passWord ) || ( 0 == passWord.Length ) || ( passWord.Length > 25 )) { System.Diagnostics.Trace.WriteLine( "[ValidateUser] Input validation of passWord failed." ); return false; } try { // Consult with your SQL Server administrator for an appropriate connection // string to use to connect to your local SQL Server. conn = new SqlConnection( "server=localhost;Integrated Security=SSPI;database=pubs" ); conn.Open(); // Create SqlCommand to select pwd field from users table given supplied userName. cmd = new SqlCommand( "Select pwd from users where uname=@userName", conn ); cmd.Parameters.Add( "@userName", SqlDbType.VarChar, 25 ); cmd.Parameters["@userName"].Value = userName; // Execute command and fetch pwd field into lookupPassword string. lookupPassword = (string) cmd.ExecuteScalar(); // Cleanup command and connection objects. cmd.Dispose(); conn.Dispose(); } catch ( Exception ex ) { // Add error handling here for debugging. // This error message should not be sent back to the caller. System.Diagnostics.Trace.WriteLine( "[ValidateUser] Exception " + ex.Message ); } // If no password found, return false. if ( null == lookupPassword ) { // You could write failed login attempts here to event log for additional security. return false; } // Compare lookupPassword and input passWord, using a case-sensitive comparison. return ( 0 == string.Compare( lookupPassword, passWord, false )); }
您可以使用兩種方法之一來產生窗體驗證 Cookie,並將使用者重新導向至事件中的
cmdLogin_ServerClick
適當頁面。 這兩個案例都提供範例程序代碼。 根據您的需求使用其中一個。RedirectFromLoginPage
呼叫 方法以自動產生窗體驗證 Cookie,並將使用者重新導向至事件中的cmdLogin_ServerClick
適當頁面:private void cmdLogin_ServerClick(object sender, System.EventArgs e) { if (ValidateUser(txtUserName.Value,txtUserPass.Value)) FormsAuthentication.RedirectFromLoginPage(txtUserName.Value, chkPersistCookie.Checked); else Response.Redirect("logon.aspx", true); }
產生驗證票證、加密、建立 Cookie、將它新增至回應,以及重新導向使用者。 這項作業可讓您更充分掌控如何建立 Cookie。 您也可以在此案例中包含自訂資料以及
FormsAuthenticationTicket
。private void cmdLogin_ServerClick(object sender, System.EventArgs e) { if (ValidateUser(txtUserName.Value,txtUserPass.Value)) { FormsAuthenticationTicket tkt; string cookiestr; HttpCookie ck; tkt = new FormsAuthenticationTicket(1, txtUserName.Value, DateTime.Now, DateTime.Now.AddMinutes(30), chkPersistCookie.Checked, "your custom data"); cookiestr = FormsAuthentication.Encrypt(tkt); ck = new HttpCookie(FormsAuthentication.FormsCookieName, cookiestr); if (chkPersistCookie.Checked) ck.Expires=tkt.Expiration; ck.Path = FormsAuthentication.FormsCookiePath; Response.Cookies.Add(ck); string strRedirect; strRedirect = Request["ReturnUrl"]; if (strRedirect==null) strRedirect = "default.aspx"; Response.Redirect(strRedirect, true); } else Response.Redirect("logon.aspx", true); }
請確定下列程式代碼已新增至
InitializeComponent
Web 表單設計工具產生的程式代碼中的 方法:this.cmdLogin.ServerClick += new System.EventHandler(this.cmdLogin_ServerClick);
建立Default.aspx頁面
本節會建立測試頁面,讓使用者在驗證之後會重新導向。 如果用戶流覽至此頁面而不先登入應用程式,則會將其重新導向至登入頁面。
將現有的 WebForm1.aspx 頁面重新命名為 Default.aspx,然後在編輯器中開啟它。
切換至 HTML 檢視,並在標記之間
<form>
複製下列程式代碼:<input type="submit" Value="SignOut" runat="server" id="cmdSignOut">
此按鈕可用來從窗體驗證會話註銷。
切換至 [設計] 檢視,然後儲存頁面。
在程式代碼後置檔案中匯入必要的命名空間:
using System.Web.Security;
按兩下 [註銷 ] 以開啟程式代碼後置頁面 (Default.aspx.cs),並在事件處理程式中
cmdSignOut_ServerClick
複製下列程式代碼:private void cmdSignOut_ServerClick(object sender, System.EventArgs e) { FormsAuthentication.SignOut(); Response.Redirect("logon.aspx", true); }
請確定下列程式代碼已新增至
InitializeComponent
Web 表單設計工具產生的程式代碼中的 方法:this.cmdSignOut.ServerClick += new System.EventHandler(this.cmdSignOut_ServerClick);
儲存並編譯專案。 您現在可以使用應用程式。
其他注意事項
您可能想要安全地將密碼儲存在資料庫中。 您可以使用名為
HashPasswordForStoringInConfigFile
的FormsAuthentication
類別公用程式函式來加密密碼,再將它們儲存在資料庫或組態檔中。您可能想要將 SQL 連線資訊儲存在組態檔 (Web.config) 中,以便在必要時輕鬆地修改它。
您可以考慮新增程序代碼,以防止嘗試使用不同密碼組合的駭客登入。 例如,您可以包含只接受兩或三次登入嘗試的邏輯。 如果使用者無法登入某些嘗試,您可能會想要在資料庫中設定旗標,使其無法登入,直到用戶流覽不同的頁面或呼叫支援行重新啟用其帳戶為止。 此外,您應該視需要新增適當的錯誤處理。
由於使用者是根據驗證 Cookie 來識別,因此您可能想要在此應用程式上使用安全套接字層 (SSL),讓任何人都無法欺騙驗證 Cookie 和正在傳輸的任何其他重要資訊。
表單式驗證需要您的用戶端在其瀏覽器中接受或啟用 Cookie。
組態區段的
<authentication>
timeout 參數會控制重新產生驗證 Cookie 的間隔。 您可以選擇提供更佳效能和安全性的值。因特網上的某些中繼 Proxy 和快取可能會快取包含
Set-Cookie
標頭的 Web 伺服器回應,然後傳回給不同的使用者。 由於窗體型驗證會使用 Cookie 來驗證使用者,因此此行為可能會導致使用者不小心(或刻意)藉由從原本不適合使用者的中繼 Proxy 或快取接收 Cookie 來模擬其他使用者。