[转]在ASP.NET中如何用C#.NET实现基于表单的验证(二)
(五)创建Logon.aspx页面
1.在已创建好的项目里创建一个新的Web 窗体,名为Logon.aspx。
2.在编辑器里打开Logon.aspx,切换到HTML视图。
3.复制下面代码,然后在编辑菜单里“选择粘贴为HTML”选项,插入到<form>标签之间。
1<h3>
2 <font face="Verdana">Logon Page</font>
3</h3>
4<table>
5 <tr>
6 <td>Email:</td>
7 <td><input id="txtUserName" type="text" runat="server"></td>
8 <td><ASP:RequiredFieldValidator ControlToValidate="txtUserName"
9 Display="Static" ErrorMessage="*" runat="server"
10 ID="vUserName" /></td>
11 </tr>
12 <tr>
13 <td>Password:</td>
14 <td><input id="txtUserPass" type="password" runat="server"></td>
15 <td><ASP:RequiredFieldValidator ControlToValidate="txtUserPass"
16 Display="Static" ErrorMessage="*" runat="server"
17 ID="vUserPass" />
18 </td>
19 </tr>
20 <tr>
21 <td>Persistent Cookie:</td>
22 <td><ASP:CheckBox id="chkPersistCookie" runat="server" autopostback="false" /></td>
23 <td></td>
24 </tr>
25</table>
26<input type="submit" Value="Logon" runat="server" ID="cmdLogin"><p></p>
27<asp:Label id="lblMsg" ForeColor="red" Font-Name="Verdana" Font-Size="10" runat="server" />
28
2 <font face="Verdana">Logon Page</font>
3</h3>
4<table>
5 <tr>
6 <td>Email:</td>
7 <td><input id="txtUserName" type="text" runat="server"></td>
8 <td><ASP:RequiredFieldValidator ControlToValidate="txtUserName"
9 Display="Static" ErrorMessage="*" runat="server"
10 ID="vUserName" /></td>
11 </tr>
12 <tr>
13 <td>Password:</td>
14 <td><input id="txtUserPass" type="password" runat="server"></td>
15 <td><ASP:RequiredFieldValidator ControlToValidate="txtUserPass"
16 Display="Static" ErrorMessage="*" runat="server"
17 ID="vUserPass" />
18 </td>
19 </tr>
20 <tr>
21 <td>Persistent Cookie:</td>
22 <td><ASP:CheckBox id="chkPersistCookie" runat="server" autopostback="false" /></td>
23 <td></td>
24 </tr>
25</table>
26<input type="submit" Value="Logon" runat="server" ID="cmdLogin"><p></p>
27<asp:Label id="lblMsg" ForeColor="red" Font-Name="Verdana" Font-Size="10" runat="server" />
28
4.切换到设计视图,保存这个页面。
(六)编写事件处理代码来验证用户身份
下面这些代码是放在后置代码页里的(Logon.aspx.cs)
1.双击Logon页面打开Logon.aspx.cs文件。
2.在后置代码文件里导入必要的名空间:
using System.Data.SqlClient;
using System.Web.Security;
3.创建一个ValidateUser的函数,通过在数据库中查找用户来验证用户的身份。(请改变数据库连接字符串来指向你的数据库)
1private bool ValidateUser( string userName, string passWord )
2{
3SqlConnection conn;
4SqlCommand cmd;
5string lookupPassword = null;
6
7// Check for invalid userName.
8// userName must not be null and must be between 1 and 15 characters.
9if ( ( null == userName ) || ( 0 == userName.Length ) || ( userName.Length > 15 ) )
10{
11 System.Diagnostics.Trace.WriteLine( "[ValidateUser] Input validation of userName failed." );
12 return false;
13}
14
15// Check for invalid passWord.
16// passWord must not be null and must be between 1 and 25 characters.
17if ( ( null == passWord ) || ( 0 == passWord.Length ) || ( passWord.Length > 25 ) )
18{
19 System.Diagnostics.Trace.WriteLine( "[ValidateUser] Input validation of passWord failed." );
20 return false;
21}
22
23try
24{
25 // Consult with your SQL Server administrator for an appropriate connection
26 // string to use to connect to your local SQL Server.
27 conn = new SqlConnection( "server=localhost;Integrated Security=SSPI;database=pubs" );
28 conn.Open();
29
30 // Create SqlCommand to select pwd field from users table given supplied userName.
31 cmd = new SqlCommand( "Select pwd from users where uname=@userName", conn );
32 cmd.Parameters.Add( "@userName", SqlDbType.VarChar, 25 );
33 cmd.Parameters["@userName"].Value = userName;
34
35 // Execute command and fetch pwd field into lookupPassword string.
36 lookupPassword = (string) cmd.ExecuteScalar();
37
38 // Cleanup command and connection objects.
39 cmd.Dispose();
40 conn.Dispose();
41}
42catch ( Exception ex )
43{
44 // Add error handling here for debugging.
45 // This error message should not be sent back to the caller.
46 System.Diagnostics.Trace.WriteLine( "[ValidateUser] Exception " + ex.Message );
47}
48
49// If no password found, return false.
50if ( null == lookupPassword )
51{
52 // You could write failed login attempts here to event log for additional security.
53 return false;
54}
55
56// Compare lookupPassword and input passWord, using a case-sensitive comparison.
57return ( 0 == string.Compare( lookupPassword, passWord, false ) );
58
59}
60
2{
3SqlConnection conn;
4SqlCommand cmd;
5string lookupPassword = null;
6
7// Check for invalid userName.
8// userName must not be null and must be between 1 and 15 characters.
9if ( ( null == userName ) || ( 0 == userName.Length ) || ( userName.Length > 15 ) )
10{
11 System.Diagnostics.Trace.WriteLine( "[ValidateUser] Input validation of userName failed." );
12 return false;
13}
14
15// Check for invalid passWord.
16// passWord must not be null and must be between 1 and 25 characters.
17if ( ( null == passWord ) || ( 0 == passWord.Length ) || ( passWord.Length > 25 ) )
18{
19 System.Diagnostics.Trace.WriteLine( "[ValidateUser] Input validation of passWord failed." );
20 return false;
21}
22
23try
24{
25 // Consult with your SQL Server administrator for an appropriate connection
26 // string to use to connect to your local SQL Server.
27 conn = new SqlConnection( "server=localhost;Integrated Security=SSPI;database=pubs" );
28 conn.Open();
29
30 // Create SqlCommand to select pwd field from users table given supplied userName.
31 cmd = new SqlCommand( "Select pwd from users where uname=@userName", conn );
32 cmd.Parameters.Add( "@userName", SqlDbType.VarChar, 25 );
33 cmd.Parameters["@userName"].Value = userName;
34
35 // Execute command and fetch pwd field into lookupPassword string.
36 lookupPassword = (string) cmd.ExecuteScalar();
37
38 // Cleanup command and connection objects.
39 cmd.Dispose();
40 conn.Dispose();
41}
42catch ( Exception ex )
43{
44 // Add error handling here for debugging.
45 // This error message should not be sent back to the caller.
46 System.Diagnostics.Trace.WriteLine( "[ValidateUser] Exception " + ex.Message );
47}
48
49// If no password found, return false.
50if ( null == lookupPassword )
51{
52 // You could write failed login attempts here to event log for additional security.
53 return false;
54}
55
56// Compare lookupPassword and input passWord, using a case-sensitive comparison.
57return ( 0 == string.Compare( lookupPassword, passWord, false ) );
58
59}
60
(注:这段代码的意思是先判断输入的用户名和密码是否符合一定的条件,如上,如果符合则连接到数据库,并且根据用户名来取出密码并返回密码,最后再判断取出的密码是否为空,如果不为空则再判断取出的密码和输入的密码是否相同,最后的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);
}
{
if(ValidateUser(txtUserName.value,txtUserPass.Value))
FormsAuthentication.RedirectFromLoginPage(txtUserName.Value,chkPresistCookie.Checked);
else
Response.Redirect("logon.aspx",true);
}
1private void cmdLogin_ServerClick(object sender,System.EventArgs e)
2{
3 if(ValidateUser(txtUserName.value,txtUserPass.Value))
4 {
5 FormsAuthenticationTicket tkt;
6 string cookiestr;
7 HttpCookie ck;
8 tkt=new FormsAuthenticationTicket(1,txtUserName.value,DateTime.Now,DateTime.Now.AddMinutes(30),chkPersistCookie.Checked,"your custom data"); //创建一个验证票据
9 cookiestr=FormsAuthentication.Encrypt(tkt);//并且加密票据
10 ck=new HttpCookie(FormsAuthentication.FormsCookieName,cookiestr);// 创建cookie
11 if(chkpersistCookie.Checked) //如果用户选择了保存密码
12 ck.Expires=tkt.Expiratioin;//设置cookie有效期
13 ck.Path=FormsAuthentication.FormsCookiePath;//cookie存放路径
14 Response.Cookies.Add(ck);
15 string strRedirect;
16 strRedirect=Request["ReturnUrl"];
17 if(strRedirect==null)
18 strRedirect="default.aspx";
19 Response.Redirect(strRedirect,true);
20 }
21 else
22 Reponse.Redirect("logon.aspx",true);
23}
24
2{
3 if(ValidateUser(txtUserName.value,txtUserPass.Value))
4 {
5 FormsAuthenticationTicket tkt;
6 string cookiestr;
7 HttpCookie ck;
8 tkt=new FormsAuthenticationTicket(1,txtUserName.value,DateTime.Now,DateTime.Now.AddMinutes(30),chkPersistCookie.Checked,"your custom data"); //创建一个验证票据
9 cookiestr=FormsAuthentication.Encrypt(tkt);//并且加密票据
10 ck=new HttpCookie(FormsAuthentication.FormsCookieName,cookiestr);// 创建cookie
11 if(chkpersistCookie.Checked) //如果用户选择了保存密码
12 ck.Expires=tkt.Expiratioin;//设置cookie有效期
13 ck.Path=FormsAuthentication.FormsCookiePath;//cookie存放路径
14 Response.Cookies.Add(ck);
15 string strRedirect;
16 strRedirect=Request["ReturnUrl"];
17 if(strRedirect==null)
18 strRedirect="default.aspx";
19 Response.Redirect(strRedirect,true);
20 }
21 else
22 Reponse.Redirect("logon.aspx",true);
23}
24
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来验证用户的,所以通过中间代理服务器或者缓冲的话可能会引起用户会被意外地搞错为原本不是要发送给他的用户。
参考文章:
如果想要知道如何通过配置<credentials>节点存放用户名和密码来实现基于表单的验证的话,请参考以下GotDotNet ASP.NET QuickStart示例:
基于表单的验证:http://www.gotdotnet.com/QuickStart/aspplus/default.aspx?url=/quickstart/aspplus/doc/formsauth.aspx
如果想要知道如何使用XML文件来存放用户名和密码来实现基于表单的验证的话,请参考SDK文档的以下示例:
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpguide/html/cpconcookieauthenticationusinganxmlusersfile.asp
如果想要知道更多的关于ASP.NET安全的话,请参考Microsoft .NET Framework Developer's Guide文档:
ASP.NET 安全: http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpguide/html/cpconaspnetwebapplicationsecurity.asp
如果想知道更多关于System.Web.Security名空间的话,请参考:
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpref/html/frlrfSystemWebSecurity.asp
如果想知道更多的关于ASP.NET配置的话,请参考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配置节点:
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpguide/html/cpgrfaspnetconfigurationsections.asp
如果想知道更多关于ASP.NET安全指导的话,请参考MSDN:
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnbda/html/authaspdotnet.asp
如果想知道更多关于ASP.NET的,请参考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)