本文介绍如何通过使用数据库存储用户来实现基于窗体的身份验证。
要求
下表概括了推荐的硬件、软件、网络设施和您需要的 Service Pack:
- Microsoft Visual Studio .NET
- Microsoft Internet 信息服务 (IIS) 5.0 或更高版本
- Microsoft SQL Server
使用 C# .NET 创建 ASP.NET 应用程序
- 打开 Visual Studio .NET。
- 新建 ASP.NET Web 应用程序,并指定应用程序的名称和位置。
在 Web.config 文件中配置安全设置
本部分演示了如何添加和修改
<身份验证>和
<授权>配置部分,以配置 ASP.NET 应用程序来使用基于表单的身份验证。
- 在项目资源管理器中,打开 Web.config 文件。
- 将身份验证模式更改为Forms(窗体)。
- 插入 <Forms> 标记,并填入相应的属性。(有关这些属性的详细信息,请参阅参考部分列出的 MSDN 文档或 QuickStart 文档。)复制以下代码,然后单击“编辑”菜单上的“粘贴为 HTML”,以粘贴文件 <authentication> 部分中的代码:
<authentication mode="Forms">
<forms name=".ASPXFORMSDEMO" loginUrl="logon.aspx"
protection="All" path="/" timeout="30" />
</authentication>
- 在 <authorization> 部分中拒绝匿名用户的访问(如下所示):
<authorization>
<deny users ="?" />
<allow users = "*" />
</authorization>
创建一个示例数据库表存储用户详细信息
本部演示了如何创建一个示例数据库表来存储用户的用户名、密码和角色。如果要将用户角色存储在数据库中并实现基于角色的安全性,则需要角色列。
- 在“开始”菜单上,单击“运行”,然后键入 notepad 以打开记事本。
- 突出显示以下 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。
- 在 Microsoft SQL Server 计算机上,在查询分析器中打开 Users.sql。在数据库列表中,单击“pubs”,然后运行该脚本。这将创建一个示例用户表,并使用此示例应用程序填充要使用的 Pubs 数据库中的该表。
创建 Logon.aspx 页
- 将一个新的 Web 窗体添加到名为 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 窗体用于向用户提供登录窗体,以便他们提供用于登录到应用程序的用户名和密码。 - 切换到“设计”视图并保存该页。
对事件处理程序编码,以便验证用户凭据
本部分介绍了位于代码隐藏页面 (Logon.aspx.cs) 的代码。
- 双击“Logon”打开 Logon.aspx.cs 文件。
- 导入代码隐藏文件中必需的命名空间:
using System.Data.SqlClient;
using System.Web.Security;
- 创建 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 ) );
}
- 可使用两种方法之一生成窗体身份验证 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);
}
- 确保将以下代码添加到由 Web 窗体设计器生成的代码的 InitializeComponent 方法中。
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;
- 双击 SignOut 打开代码隐藏页 (Default.aspx.cs),并在 cmdSignOut_ServerClick 事件处理程序中复制以下代码:
private void cmdSignOut_ServerClick(object sender, System.EventArgs e)
{
FormsAuthentication.SignOut();
Response.Redirect("logon.aspx", true);
}
- 请确保以下代码已添加到 Web 窗体设计器生成的代码的 InitializeComponent 方法中:
this.cmdSignOut.ServerClick += new System.EventHandler(this.cmdSignOut_ServerClick);
- 保存并编译项目。现在,您就可以使用该应用程序了。
其他说明
- 您可能希望将密码安全地存储在数据库中。在将密码存储在数据库或配置文件中之前,可以使用名为 HashPasswordForStoringInConfigFile 的 FormsAuthentication 类实用工具函数对密码进行加密。
- 您可能希望将 SQL 连接信息存储在配置文件 (Web.config) 中,以便在需要时方便地修改它。
- 也可以考虑添加代码,以防黑客使用不同的密码组合进行登录。例如,可以包含一个只允许两次或三次登录尝试的逻辑。如果用户在尝试特定次数后无法登录,您可能希望在数据库中设置一个标志以禁止此用户登录,直到此用户通过访问另外一个页面或拨打您的支持电话重新启用其帐户时为止。另外,还应根据需要添加相应的错误处理代码。
- 因为用户是基于身份验证 Cookie 来标识的,所以您可能希望在此应用程序上使用安全套接字层 (SSL),这样任何人都无法骗取身份验证 Cookie 和传输的任何其他重要信息。
- 基于窗体的身份验证要求客户机在其浏览器上接受或启用 Cookie。
- <authentication> 配置部分的 timeout 参数控制重新生成身份验证 Cookie 的时间间隔。您可以选择一个能提供较好性能和安全性的值。
- Internet 上的某些中间代理和缓存可能会将包含“设置 Cookie”标题的 Web 服务器响应缓存起来,然后将其返回给另外一个用户。因为基于窗体身份验证使用 cookie 来验证用户身份,所以这可能导致用户通过接收到由中间代理或缓存提供的本不是要发送给他们的 cookie 而意外(或有意地)模拟另外的用户。
有关如何使用
<credentials> 部分存储用户和密码来实现基于窗体的简单身份验证的更多信息,请参阅以下 GotDotNet ASP.NET 快速入门示例:
有关如何使用 XML 文件实现基于表单的身份验证,以存储用户和密码的详细信息,请参阅 .NET Framework 软件开发工具包 (SDK) 文档中的以下主题:
有关 ASP.NET Web 应用程序安全的详细信息,请参阅以下 Microsoft .NET Framework 设计器的指南文档:
有关
System.Web.Security 命名空间的详细信息,请参阅以下 Microsoft .NET Framework 参考文档:
有关 ASP.NET 配置的详细信息,请参阅以下 Microsoft .NET Framework 设计器的指南文章:
有关 ASP.NET 安全指导的详细信息:请参阅以下 MSDN 白皮书:
有关 ASP.NET 的常规信息,请参阅以下 MSDN 新闻组:
microsoft.public.dotnet.framework.aspnet
(http://msdn.microsoft.com/newsgroups/default.aspx?query=microsoft.public.dotnet.framework.aspnet&dg=&cat=en-us-msdn&lang=en&cr=US&pt=&catlist=774F24A2-F71F-425F-AC2B-DC48AB0DA5C9&dglist=&ptlist=&exp=&sloc=en-us)
注意:本篇“快速发布”文章是从 Microsoft 支持组织直接创建的。 文中包含的信息按原样提供,用于响应紧急问题。 由于发布仓促,材料可能包含印刷错误,并且可能随时修订,恕不另行通知。 有关其他注意事项,请参阅
使用条款
(http://go.microsoft.com/fwlink/?LinkId=151500)
。
文章编号: 301240 - 最后修改: 2012年6月29日 - 修订: 1.0
这篇文章中的信息适用于:
- Microsoft ASP.NET 1.1
- Microsoft Visual C# .NET 2003 标准版
- Microsoft ASP.NET 1.0
- Microsoft Visual C# .NET 2002 标准版
- Microsoft SQL Server 2000 标准版
- Microsoft SQL Server 7.0 标准版
- Microsoft SQL Server 2000 64-bit Edition
| kbconfig kbhowtomaster kbsecurity kbweb KB301240 |
Microsoft和/或其各供应商对于为任何目的而在本服务器上发布的文件及有关图形所含信息的适用性,不作任何声明。 所有该等文件及有关图形均"依样"提供,而不带任何性质的保证。Microsoft和/或其各供应商特此声明,对所有与该等信息有关的保证和条件不负任何责任,该等保证和条件包括关于适销性、符合特定用途、所有权和非侵权的所有默示保证和条件。在任何情况下,在由于使用或运行本服务器上的信息所引起的或与该等使用或运行有关的诉讼中,Microsoft和/或其各供应商就因丧失使用、数据或利润所导致的任何特别的、间接的、衍生性的损害或任何因使用而丧失所导致的之损害、数据或利润不负任何责任。