使用 C#.NET 在 ASP.NET 应用程序中实现基于表单的身份验证

本文演示如何通过使用数据库来存储用户来实现基于表单的身份验证。 它引用以下 Microsoft .NET Framework类库命名空间:

  • System.Data.SqlClient
  • System.Web.Security

原始产品版本: ASP.NET
原始 KB 编号: 301240

要求

以下列表概述了所需的建议的硬件、软件、网络基础结构和 Service Pack:

  • Visual Studio .NET
  • Internet Information Services (IIS) 5.0 或更高版本
  • SQL Server

使用 C# .NET 创建 ASP.NET 应用程序

  1. 打开 Visual Studio .NET。
  2. 创建新的 ASP.NET Web 应用程序,并指定名称和位置。

在 Web.config 文件中配置安全设置

本部分演示如何添加和修改 <authentication><authorization> 配置部分,以将 ASP.NET 应用程序配置为使用基于表单的身份验证。

  1. 在“解决方案资源管理器”中打开 Web.config 文件。

  2. 将身份验证模式更改为 Forms

  3. <Forms>插入 标记,并填充相应的属性。 复制以下代码,然后在“编辑”菜单上选择“粘贴为 HTML”,将代码粘贴到<authentication>文件的 部分中:

    <authentication mode="Forms">
        <forms name=".ASPXFORMSDEMO" loginUrl="logon.aspx"
            protection="All" path="/" timeout="30" />
    </authentication>
    
  4. 拒绝访问节中的 <authorization> 匿名用户,如下所示:

    <authorization>
        <deny users ="?" />
        <allow users = "*" />
    </authorization>
    

创建用于存储用户详细信息的示例数据库表

本部分介绍如何创建示例数据库来存储用户的用户名、密码和角色。 如果要在数据库中存储用户角色并实现基于角色的安全性,则需要角色列。

  1. “开始 ”菜单上,选择“ 运行”,然后键入记事本以打开记事本。

  2. 突出显示以下 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
    
  3. 将文件另存为 Users.sql

  4. 在SQL Server计算机上,在查询分析器中打开Users.sql。 从数据库列表中,选择“ 发布”,然后运行脚本。 此操作将创建一个示例用户表,并填充 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" />
    

    此 Web 表单用于向用户显示登录表单,以便他们提供登录应用程序的用户名和密码。

  4. 切换到“设计”视图,然后保存页面。

对事件处理程序进行编码,以便验证用户凭据

本部分介绍放置在代码隐藏页中的代码 (Logon.aspx.cs) 。

  1. 双击“ 登录 ”打开 Logon.aspx.cs 文件。

  2. 在代码隐藏文件中导入所需的命名空间:

    using System.Data.SqlClient;
    using System.Web.Security;
    
  3. 创建一个 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 ));
    }
    
  4. 可以使用两种方法之一来生成表单身份验证 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);
      }
      
  5. 请确保将以下代码添加到 InitializeComponent Web 窗体Designer生成的代码中的 方法:

    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. 双击“ 注销 ”打开代码隐藏页 (Default.aspx.cs) ,并在事件处理程序中 cmdSignOut_ServerClick 复制以下代码:

    private void cmdSignOut_ServerClick(object sender, System.EventArgs e)
    {
        FormsAuthentication.SignOut();
        Response.Redirect("logon.aspx", true);
    }
    
  6. 请确保将以下代码添加到 InitializeComponent Web 窗体Designer生成的代码中的 方法:

    this.cmdSignOut.ServerClick += new System.EventHandler(this.cmdSignOut_ServerClick);
    
  7. 保存并编译项目。 现在可以使用应用程序。

其他说明

  • 你可能希望安全地将密码存储在数据库中。 在将密码存储在数据库或配置文件中之前,可以使用 FormsAuthentication 名为 的 HashPasswordForStoringInConfigFile 类实用工具函数对密码进行加密。

  • 可能需要将 SQL 连接信息存储在配置文件中, (Web.config) ,以便在必要时轻松对其进行修改。

  • 可以考虑添加代码,以防止尝试使用不同密码组合的黑客登录。 例如,可以包含仅接受两次或三次登录尝试的逻辑。 如果用户在某些尝试中无法登录,你可能希望在数据库中设置一个标志,以在用户通过访问其他页面或调用支持热线重新启用其帐户之前,不允许他们登录。 此外,应根据需要添加适当的错误处理。

  • 由于用户是基于身份验证 Cookie 进行标识的,因此你可能想要在此应用程序上使用安全套接字层 (SSL) ,以便任何人都无法欺骗正在传输的身份验证 Cookie 和任何其他有价值的信息。

  • 基于表单的身份验证要求客户端在其浏览器中接受或启用 Cookie。

  • 配置部分的 <authentication> timeout 参数控制重新生成身份验证 Cookie 的间隔。 可以选择一个提供更好的性能和安全性的值。

  • Internet 上的某些中介代理和缓存可能会缓存包含 Set-Cookie 标头的 Web 服务器响应,这些标头随后会返回到其他用户。 由于基于表单的身份验证使用 Cookie 对用户进行身份验证,因此此行为可能会导致用户意外 (或故意) 通过从中间代理或缓存接收最初不适合用户的 Cookie 来模拟其他用户。

References