| 这篇文章引用到了Microsoft .NET类库中的以下名空间: System.Data.SqlClient
 System.Web.Security
 
 这篇文章示范了如何实现通过数据库存储用户信息来实现基于表单的验证.
 (一)要求
 需要以下工具来实现
 1.Microsoft Visual Studio.NET
 2.Microsoft Internet Information Services(IIS) version 5.0 
                    或者更新
 3.Microsoft SQL Server
 (二)用C#.NET创建ASP.NET应用程序
 1.打开Visual Studio.NET
 2.建立一个新的ASP.NET Web应用程序,并且指定名称和路径.
 (三)在Web.config文件里配置安全设置
 这一节示范了如何通过添加和修改<authentication>和<authorization>节点来配置ASP.NET应用程序以实现基于表单的验证.
 1.在解决方案窗口里,打开Web.config文件.
 2.把authentication模式改为Forms(注:默认为windows)
 3.插入<Forms>标签,并且填入适当的属性.(请链接到在文章最后列出的MSDN文档或者QuickStart文档来查看这些属性)先复制下面的代码,接着再把它粘贴到<authentication>节:
 
                      
                        | <authentication 
                          mode="Forms"> <form name=".ASPXFORMSDEMO" loginUrl="logon.aspx" 
                          protection="All" path="/" timeout="30"/>
 </authentication>
 |  (注:如果不指定loginUrl,默认为default.aspx)
  4.通过加入以下节点实现拒绝匿名访问:
 
                      
                        | <authentication 
                          mode="Forms"> <form name=".ASPXFORMSDEMO" loginUrl="logon.aspx" 
                          protection="All" path="/" timeout="30"/>
 </authentication>
 |    (四)创建一个数据库表样例来存放用户资料这一节示范了如何创建一个示例数据库来存放用户名,密码,和用户角色.如果你想要实现基于角色的安全就有必要在数据库中添加一个存放用户角色的字段.
 1.打开记事本。
 2.把下面这段脚本复制到记事本然后保存:
 
                      
                        | 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,打开查询分析器,在数据库列表里选择Pubs数据库,然后把上面的脚本粘贴过来,运行。这时会在Pubs数据库里创建一个将会在这个示例程序中用到的示例用户表。
 (五)创建Logon.aspx页面
 1.在已创建好的项目里创建一个新的Web 窗体,名为Logon.aspx。
 2.在编辑器里打开Logon.aspx,切换到HTML视图。
 3.复制下面代码,然后在编辑菜单里“选择粘贴为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" />
 |     这个页面用来显示一个登录表单以便用户可以提供他们的用户名和密码,并且记录到应用程序中。(六)编写事件处理代码来验证用户身份4.切换到设计视图,保存这个页面。
 下面这些代码是放在后置代码页里的(Logon.aspx.cs)
 1.双击Logon页面打开Logon.aspx.cs文件。
 2.在后置代码文件里导入必要的名空间:
 
 
                      
                        | using System.Data.SqlClient; using System.Web.Security;
 |  3.创建一个ValidateUser的函数,通过在数据库中查找用户来验证用户的身份。(请改变数据库连接字符串来指向你的数据库)
 
 
                      
                        | 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 ) );
 } |  (注:这段代码的意思是先判断输入的用户名和密码是否符合一定的条件,如上,如果符合则连接到数据库,并且根据用户名来取出密码并返回密码,最后再判断取出的密码是否为空,如果不为空则再判断取出的密码和输入的密码是否相同,最后的false参数为不区分大小写)
  4.在cmdLogin_ServerLick事件里使用下面两种方法中的一种来产生表单验证的cookie并将页面转到指定的页面。下面提供了两种方法的示例代码,根据你的需要来选择。
 a)在cmdLogin_ServerClick事件里调用RedirectFromLoginPage方法来自动产生表单验证cookie且将页面定向到一个指定的页面。
 
 
                      
                        | 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)产生加密验证票据,创建回应的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);// 
                          创建cookie
 if(chkpersistCookie.Checked) //如果用户选择了保存密码
 ck.Expires=tkt.Expiratioin;//设置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.请确保在InititalizeComponent方法里有如下代码:
 
 
                      
                        | this.cmdLogin.ServerClick 
                          += new System.EventHandler(this.cmdLogin_ServerClick); |  (七)创建一个Default.aspx页面
 这一节创建一个测试页面用来作为当用户验证完之后重定向到的页面。如果用户第一次没有被记录下来就浏览到这个页,这时用户将被重定向到登录页面。
 1.把现有的WebForm1.aspx重命名为Default.aspx,然后在编辑器里打开。
 2.切换到HTML视图,复制以下代码到<form>标签之间:
 
                      
                        | <input type="submit" 
                          Value="SignOut" runat="server" id="cmdSignOut"> |  这个按钮用来注销表单验证会话。
 3.切换到设计视图,保存页面。
 4.在后置代码里导入必要的名空间:
 
                      
                        | using System.Web.Security; |   5.双击SingOut按钮打开后置代码(Default.aspx.cs),然后把下面代码复制到cmdSingOut_ServerClick事件处理中: 
                      
                        | private void cmdSignOut_ServerClick(object 
                          sender,System.EventArgs e) {
 FormsAuthentication.SignOut();//注销
 Response.Redirect("logon.aspx",true);
 }
 |   6.请确认在InititalizeComponent方法中有以下代码: 
                      
                        | this.cmdSignOut.ServerClick 
                          += new System.EventHandler(this.cmdSignOut_ServerClick); |   7.保存编译项目,现在可以运行这个应用程序了。(八)附加提示
 1.如果想要在数据库里安全地存放密码,可以在存放到数据到之前先用FormsAuthentication类里的HashPasswordForStoringInConfigFile函数来加密。(注:将会产生一个哈希密码)
 2.可以在配置文件(Web.config)里存放SQL连接信息,以便当需要时方便修改。
 3.可以增加一些代码来防止黑客使用穷举法来进行登录。例如,增加一些逻辑使用户只能有两三次的登录机会。如果用户在指定的登录次数里无法登录的话,可以在数据库里设置一个标志符来防止用户登录直到此用户访问另一个页面或者请示你的帮助。另外,也可以在需要时增加一些适当的错误处理。
 4.因为用户是基于验证cookie来识别的,所以可以在应用程序里使用安全套接层(SSL)来保护验证cookie和其它有用的信息。
 5.基于表单的验证方式要求客户端的游览器接受或者启用cookies.
 6.在<authentication>配置节里的timeout参数用来控制验证cookies重新产生的间隔时间。可以给它赋一个适当的值来提供更好的性能和安全性。
 7.在Internet上的一些代理服务器或者缓冲可能会缓存一些将会重新返回给另外一个用户的包含Set-Cookie头的Web服务器响应。因为基于表单的验证是使用cookie来验证用户的,所以通过中间代理服务器或者缓冲的话可能会引起用户会被意外地搞错为原本不是要发送给他的用户。
 
 
 
 |