[藍(lán)]如何用C#.NET實(shí)現(xiàn)基于表單的驗(yàn)證

2010-08-28 10:44:58來(lái)源:西部e網(wǎng)作者:

這篇文章引用到了Microsoft .NET類庫(kù)中的以下名空間:
System.Data.SqlClient
System.Web.Security
-------------------------------
任務(wù):
摘要: 
  1.要求
  2.用Visual C#.NET 創(chuàng)建一個(gè)ASP.NET 應(yīng)用程序
  3.在Web.config文件里配置安全設(shè)置
  4.創(chuàng)建一個(gè)數(shù)據(jù)庫(kù)表樣例來(lái)存放用戶資料
  5.創(chuàng)建Logon.aspx頁(yè)面
  6.編寫事件處理代碼來(lái)驗(yàn)證用戶身份
  7.創(chuàng)建一個(gè)Default.aspx頁(yè)面
  8.附加提示
 參考文章
-------------------------------
摘要
 這篇文章示范了如何實(shí)現(xiàn)通過數(shù)據(jù)庫(kù)存儲(chǔ)用戶信息來(lái)實(shí)現(xiàn)基于表單的驗(yàn)證.
(一)要求
 需要以下工具來(lái)實(shí)現(xiàn)
 1.Microsoft Visual Studio.NET
 2.Microsoft Internet Information Services(IIS) version 5.0 或者更新
 3.Microsoft SQL Server
(二)用C#.NET創(chuàng)建ASP.NET應(yīng)用程序
 1.打開Visual Studio.NET
 2.建立一個(gè)新的ASP.NET Web應(yīng)用程序,并且指定名稱和路徑.
(三)在Web.config文件里配置安全設(shè)置
這一節(jié)示范了如何通過添加和修改<authentication>和<authorization>節(jié)點(diǎn)來(lái)配置ASP.NET應(yīng)用程序以實(shí)現(xiàn)基于表單的驗(yàn)證.
 1.在解決方案窗口里,打開Web.config文件.
 2.把a(bǔ)uthentication模式改為Forms(注:默認(rèn)為windows)
 3.插入<Forms>標(biāo)簽,并且填入適當(dāng)?shù)膶傩裕ㄕ?qǐng)鏈接到在文章最后列出的MSDN文檔或者QuickStart文檔來(lái)查看這些屬性)先復(fù)制下面的代碼,接著再把它粘貼到<authentication>節(jié):

<authentication mode="Forms">
 <form name=".ASPXFORMSDEMO" loginUrl="logon.aspx" protection="All" path="/" timeout="30"/>
</authentication>
(注:如果不指定loginUrl,默認(rèn)為default.aspx)

 4.通過加入以下節(jié)點(diǎn)實(shí)現(xiàn)拒絕匿名訪問:
<authentication>
 <deny users="?"/>
 <allow users="*"/>
</authentication>

(四)創(chuàng)建一個(gè)數(shù)據(jù)庫(kù)表樣例來(lái)存放用戶資料
這一節(jié)示范了如何創(chuàng)建一個(gè)示例數(shù)據(jù)庫(kù)來(lái)存放用戶名,密碼,和用戶角色.如果你想要實(shí)現(xiàn)基于角色的安全就有必要在數(shù)據(jù)庫(kù)中添加一個(gè)存放用戶角色的字段.
 1.打開記事本。
 2.把下面這段腳本復(fù)制到記事本然后保存:

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
 3.打開Microsoft SQL Server,打開查詢分析器,在數(shù)據(jù)庫(kù)列表里選擇Pubs數(shù)據(jù)庫(kù),然后把上面的腳本粘貼過來(lái),運(yùn)行。這時(shí)會(huì)在Pubs數(shù)據(jù)庫(kù)里創(chuàng)建一個(gè)將會(huì)在這個(gè)示例程序中用到的示例用戶表。
(五)創(chuàng)建Logon.aspx頁(yè)面
 1.在已創(chuàng)建好的項(xiàng)目里創(chuàng)建一個(gè)新的Web 窗體,名為L(zhǎng)ogon.aspx。
 2.在編輯器里打開Logon.aspx,切換到HTML視圖。
 3.復(fù)制下面代碼,然后在編輯菜單里“選擇粘貼為HTML”選項(xiàng),插入到<form>標(biāo)簽之間。
<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" />

 這個(gè)頁(yè)面用來(lái)顯示一個(gè)登錄表單以便用戶可以提供他們的用戶名和密碼,并且記錄到應(yīng)用程序中。
 4.切換到設(shè)計(jì)視圖,保存這個(gè)頁(yè)面。

(六)編寫事件處理代碼來(lái)驗(yàn)證用戶身份
 下面這些代碼是放在后置代碼頁(yè)里的(Logon.aspx.cs)
 1.雙擊Logon頁(yè)面打開Logon.aspx.cs文件。
 2.在后置代碼文件里導(dǎo)入必要的名空間:
  using System.Data.SqlClient;
  using System.Web.Security;
 3.創(chuàng)建一個(gè)ValidateUser的函數(shù),通過在數(shù)據(jù)庫(kù)中查找用戶來(lái)驗(yàn)證用戶的身份。(請(qǐng)改變菘飭幼址粗趕蚰愕氖菘猓?BR>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 ) );

}
(注:這段代碼的意思是先判斷輸入的用戶名和密碼是否符合一定的條件,如上,如果符合則連接到數(shù)據(jù)庫(kù),并且根據(jù)用戶名來(lái)取出密碼并返回密碼,最后再判斷取出的密碼是否為空,如果不為空則再判斷取出的密碼和輸入的密碼是否相同,最后的false參數(shù)為不區(qū)分大小寫)

 4.在cmdLogin_ServerLick事件里使用下面兩種方法中的一種來(lái)產(chǎn)生表單驗(yàn)證的cookie并將頁(yè)面轉(zhuǎn)到指定的頁(yè)面。
下面提供了兩種方法的示例代碼,根據(jù)你的需要來(lái)選擇。
 a)在cmdLogin_ServerClick事件里調(diào)用RedirectFromLoginPage方法來(lái)自動(dòng)產(chǎn)生表單驗(yàn)證cookie且將頁(yè)面定向到一個(gè)指定的頁(yè)面。
 private void cmdLogin_ServerClick(object sender,System.EventArgs e)
 {
  if(ValidateUser(txtUserName.value,txtUserPass.Value))

   FormsAuthentication.RedirectFromLoginPage(txtUserName.Value,chkPresistCookie.Checked);
   else
    Response.Redirect("logon.aspx",true);  

 }

 b)產(chǎn)生加密驗(yàn)證票據(jù),創(chuàng)建回應(yīng)的cookie,并且重定向用戶。這種方式給了更多的控制權(quán)去讓你如何去創(chuàng)建cookie,你也可以連同F(xiàn)ormsAuthenticationTicket一起包含一些自定義的數(shù)據(jù)。
 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"); //創(chuàng)建一個(gè)驗(yàn)證票據(jù)
   cookiestr=FormsAuthentication.Encrypt(tkt);//并且加密票據(jù)
   ck=new HttpCookie(FormsAuthentication.FormsCookieName,cookiestr);// 創(chuàng)建cookie
   if(chkpersistCookie.Checked) //如果用戶選擇了保存密碼
    ck.Expires=tkt.Expiratioin;//設(shè)置cookie有效期
    ck.Path=FormsAuthentication.FormsCookiePath;//cookie存放路徑
   Response.Cookies.Add(ck);
   string strRedirect;
   strRedirect=Request["ReturnUrl"];
   if(strRedirect==null)
    strRedirect="default.aspx";
   Response.Redirect(strRedirect,true);
  }
  else
   Reponse.Redirect("logon.aspx",true);
 }
  5.請(qǐng)確保在InititalizeComponent方法里有如下代碼:
   this.cmdLogin.ServerClick += new System.EventHandler(this.cmdLogin_ServerClick);
 
(七)創(chuàng)建一個(gè)Default.aspx頁(yè)面
 這一節(jié)創(chuàng)建一個(gè)測(cè)試頁(yè)面用來(lái)作為當(dāng)用戶驗(yàn)證完之后重定向到的頁(yè)面。如果用戶第一次沒有被記錄下來(lái)就瀏覽到這個(gè)頁(yè),這時(shí)用戶將被重定向到登錄頁(yè)面。
  1.把現(xiàn)有的WebForm1.aspx重命名為Default.aspx,然后在編輯器里打開。

  2.切換到HTML視圖,復(fù)制以下代碼到<form>標(biāo)簽之間:
 <input type="submit" Value="SignOut" runat="server" id="cmdSignOut">
這個(gè)按鈕用來(lái)注銷表單驗(yàn)證會(huì)話。
  3.切換到設(shè)計(jì)視圖,保存頁(yè)面。
  4.在后置代碼里導(dǎo)入必要的名空間:
 using System.Web.Security;
  5.雙擊SingOut按鈕打開后置代碼(Default.aspx.cs),然后把下面代碼復(fù)制到cmdSingOut_ServerClick事件處理中:
  private void cmdSignOut_ServerClick(object sender,System.EventArgs e)
  {
   FormsAuthentication.SignOut();//注銷
   Response.Redirect("logon.aspx",true);
  }
  6.請(qǐng)確認(rèn)在InititalizeComponent方法中有以下代碼:
  this.cmdSignOut.ServerClick += new System.EventHandler(this.cmdSignOut_ServerClick);
  7.保存編譯項(xiàng)目,現(xiàn)在可以運(yùn)行這個(gè)應(yīng)用程序了。
(八)附加提示
  1.如果想要在數(shù)據(jù)庫(kù)里安全地存放密碼,可以在存放到數(shù)據(jù)到之前先用FormsAuthentication類里的HashPasswordForStoringInConfigFile函數(shù)來(lái)加密。(注:將會(huì)產(chǎn)生一個(gè)哈希密碼)
  2.可以在配置文件(Web.config)里存放SQL連接信息,以便當(dāng)需要時(shí)方便修改。
  3.可以增加一些代碼來(lái)防止黑客使用窮舉法來(lái)進(jìn)行登錄。例如,增加一些邏輯使用戶只能有兩三次的登錄機(jī)會(huì)。如果用戶在指定的登錄次數(shù)里無(wú)法登錄的話,可以在數(shù)據(jù)庫(kù)里設(shè)置一個(gè)標(biāo)志符來(lái)防止用戶登錄直到此用戶訪問另一個(gè)頁(yè)面或者請(qǐng)示你的幫助。另外,也可以在需要時(shí)增加一些適當(dāng)?shù)腻e(cuò)誤處理。
  4.因?yàn)橛脩羰腔隍?yàn)證cookie來(lái)識(shí)別的,所以可以在應(yīng)用程序里使用安全套接層(SSL)來(lái)保護(hù)驗(yàn)證cookie和其它有用的信息。
  5.基于表單的驗(yàn)證方式要求客戶端的游覽器接受或者啟用cookies.
  6.在<authentication>配置節(jié)里的timeout參數(shù)用來(lái)控制驗(yàn)證cookies重新產(chǎn)生的間隔時(shí)間?梢越o它賦一個(gè)適當(dāng)?shù)闹祦?lái)提供更好的性能和安全性。
  7.在Internet上的一些代理服務(wù)器或者緩沖可能會(huì)緩存一些將會(huì)重新返回給另外一個(gè)用戶的包含Set-Cookie頭的Web服務(wù)器響應(yīng)。因?yàn)榛诒韱蔚尿?yàn)證是使用cookie來(lái)驗(yàn)證用戶的,所以通過中間代理服務(wù)器或者緩沖的話可能會(huì)引起用戶會(huì)被意外地搞錯(cuò)為原本不是要發(fā)送給他的用戶。
  
  
 參考文章:
  如果想要知道如何通過配置<credentials>節(jié)點(diǎn)存放用戶名和密碼來(lái)實(shí)現(xiàn)基于表單的驗(yàn)證的話,請(qǐng)參考以下GotDotNet ASP.NET QuickStart示例:
  基于表單的驗(yàn)證:http://www.gotdotnet.com/QuickStart/aspplus/default.aspx?url=/quickstart/aspplus/doc/formsauth.aspx
  如果想要知道如何使用XML文件來(lái)存放用戶名和密碼來(lái)實(shí)現(xiàn)基于表單的驗(yàn)證的話,請(qǐng)參考SDK文檔的以下示例:
 http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpguide/html/cpconcookieauthenticationusinganxmlusersfile.asp
   如果想要知道更多的關(guān)于ASP.NET安全的話,請(qǐng)參考Microsoft .NET Framework Developer's Guide文檔:
ASP.NET 安全:  http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpguide/html/cpconaspnetwebapplicationsecurity.asp
   如果想知道更多關(guān)于System.Web.Security名空間的話,請(qǐng)參考:
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpref/html/frlrfSystemWebSecurity.asp
   如果想知道更多的關(guān)于ASP.NET配置的話,請(qǐng)參考Microsoft .NET Framework Developer's Guide文檔:
ASP.NET配置:
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpguide/html/cpconaspnetconfiguration.asp
ASP.NET配置節(jié)點(diǎn):
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpguide/html/cpgrfaspnetconfigurationsections.asp
  如果想知道更多關(guān)于ASP.NET安全指導(dǎo)的話,請(qǐng)參考MSDN:
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnbda/html/authaspdotnet.asp
  如果想知道更多關(guān)于ASP.NET的,請(qǐng)參考MSDN新聞組:
http://go.microsoft.com/fwlink/?linkid=5811&clcid=0x409

 這篇文章適用于:
Microsoft ASP.NET (included with the .NET Framework 1.1)
Microsoft Visual C# .NET (2003)
Microsoft ASP.NET (included with the .NET Framework) 1.0
Microsoft Visual C# .NET (2002)
Microsoft SQL Server 2000 (all editions)
Microsoft SQL Server 7.0
Microsoft SQL Server 2000 64 bit (all editions)
 
原文鏈接:http://support.microsoft.com/default.aspx?scid=kb;en-us;301240

 

關(guān)鍵詞:C#

贊助商鏈接: