使用 C#.NET 在 ASP.NET 应用程序中实现基于表单的身份验证
本文演示如何使用数据库存储用户来实现基于表单的身份验证。 它引用以下Microsoft .NET Framework 类库命名空间:
System.Data.SqlClient
System.Web.Security
原始产品版本: ASP.NET
原始 KB 编号: 301240
要求
以下列表概述了所需的推荐硬件、软件、网络基础结构和服务包:
- Visual Studio .NET
- Internet Information Services (IIS) 版本 5.0 或更高版本
- SQL Server
使用 C# .NET 创建 ASP.NET 应用程序
- 打开 Visual Studio .NET。
- 创建新的 ASP.NET Web 应用程序,并指定名称和位置。
在 Web.config 文件中配置安全设置
本部分演示如何添加和修改和<authorization>
配置<authentication>
部分,以配置 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 并运行脚本。 此操作将创建一个示例用户表,并在 Pubs 数据库中填充要与此示例应用程序一起使用的表。
创建Logon.aspx页
向名为 Logon.aspx的项目添加新的 Web 窗体。
在编辑器中打开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);
保存并编译项目。 现在可以使用该应用程序。
其他注释
你可能想要安全地将密码存储在数据库中。 可以在将密码存储在数据库或配置文件中之前,使用
FormsAuthentication
命名HashPasswordForStoringInConfigFile
的类实用工具函数对密码进行加密。可能需要将 SQL 连接信息存储在配置文件(Web.config)中,以便根据需要轻松修改它。
可以考虑添加代码以防止尝试使用不同密码组合的黑客登录。 例如,可以包含仅接受两次或三次登录尝试的逻辑。 如果用户在某些尝试中无法登录,则可能需要在数据库中设置一个标志,以禁止他们登录,直到用户通过访问其他页面或通过调用支持行重新启用其帐户。 此外,应根据需要添加适当的错误处理。
由于用户是根据身份验证 Cookie 标识的,因此你可能想要在此应用程序上使用安全套接字层(SSL),以便没有人能够欺骗身份验证 Cookie 和正在传输的任何其他有价值的信息。
基于表单的身份验证要求客户端在其浏览器中接受或启用 Cookie。
配置节的
<authentication>
超时参数控制重新生成身份验证 Cookie 的间隔。 可以选择一个可提供更好性能和安全性的值。Internet 上的某些中间代理和缓存可能会缓存包含
Set-Cookie
标头的 Web 服务器响应,这些响应随后会返回到其他用户。 由于基于表单的身份验证使用 Cookie 对用户进行身份验证,因此此行为可能会导致用户意外(或有意)通过从最初不适合用户的中介代理或缓存接收 Cookie 来模拟其他用户。