解锁和审批用户帐户 (C#)

作者 :Scott Mitchell

注意

自本文撰写以来,ASP.NET 成员资格提供程序已被 ASP.NET Identity 取代。 强烈建议更新应用以使用 ASP.NET 标识 平台,而不是本文撰写时介绍的成员资格提供程序。 ASP.NET 标识比 ASP.NET 成员身份系统具有许多优势,包括:

  • 性能更好
  • 改进了可扩展性和可测试性
  • 支持 OAuth、OpenID Connect 和双因素身份验证
  • 基于声明的标识支持
  • 更好地与 ASP.Net Core 的互操作性

本教程介绍如何为管理员生成网页,以管理用户的锁定状态和已批准状态。 我们还将了解如何仅在新用户验证其电子邮件地址后批准他们。

简介

除了用户名、密码和电子邮件外,每个用户帐户都有两个状态字段,用于指示用户是否可以登录站点:已锁定和已批准。 如果用户在指定的分钟数内以指定次数提供无效凭据, (默认设置会在 10 分钟内 5 次无效登录尝试后锁定用户,) 。 在新用户能够登录到站点之前必须执行某些操作的情况下,“已批准”状态非常有用。 例如,用户可能需要先验证其电子邮件地址或获得管理员的批准,然后才能登录。

由于锁定或未经批准的用户无法登录,因此很自然地想知道如何重置这些状态。 ASP.NET 不包括用于管理用户的锁定状态和已批准状态的任何内置功能或 Web 控件,部分原因是需要逐个站点处理这些决策。 某些网站可能会自动批准所有新用户帐户 (默认行为) 。 其他人则拥有管理员批准新帐户,或者在用户访问发送到注册时提供的电子邮件地址的链接之前不批准用户。 同样,某些网站可能会锁定用户,直到管理员重置其状态,而其他网站会向锁定的用户发送电子邮件,其中包含他们可以访问的 URL 来解锁其帐户。

本教程介绍如何为管理员生成网页,以管理用户的锁定状态和已批准状态。 我们还将了解如何仅在新用户验证其电子邮件地址后批准他们。

步骤 1:管理用户的锁定状态和已批准状态

生成接口以从多个中选择一个用户帐户的教程中,我们构造了一个页面,其中列出了分页筛选的 GridView 中的每个用户帐户。 该网格列出每个用户的姓名和电子邮件、其已批准和锁定状态、他们当前是否处于联机状态,以及有关用户的任何评论。 若要管理用户的已批准和锁定状态,我们可以使此网格可编辑。 若要更改用户的已批准状态,管理员将首先找到用户帐户,然后编辑相应的 GridView 行,选中或取消选中“已批准”复选框。 或者,我们可以通过单独的 ASP.NET 页面管理已批准和锁定状态。

本教程使用两个 ASP.NET 页: ManageUsers.aspxUserInformation.aspx。 此处的想法是列出 ManageUsers.aspx 系统中的用户帐户,同时 UserInformation.aspx 使管理员能够管理特定用户的已批准和锁定状态。 我们的第一个业务顺序是增加 GridView, ManageUsers.aspx 以包含 HyperLinkField,该字段呈现为链接列。 我们希望每个链接指向 UserInformation.aspx?user=UserName,其中 UserName 是要编辑的用户的名称。

注意

如果下载了“恢复和更改密码”教程的代码,你可能已注意到该ManageUsers.aspx页面已包含一组“管理”链接,并且该UserInformation.aspx页提供了用于更改所选用户密码的界面。 我决定不在与本教程关联的代码中复制该功能,因为它的工作原理是绕过成员资格 API,并直接使用 SQL Server 数据库来更改用户的密码。 本教程从页面的头开始 UserInformation.aspx

打开页面并将 ManageUsers.aspx HyperLinkField 添加到 UserAccounts GridView。 将 HyperLinkField 的 Text 属性分别设置为“Manage”及其 DataNavigateUrlFieldsDataNavigateUrlFormatString 属性 UserName 和“UserInformation.aspx?user={0}”。 这些设置配置 HyperLinkField,以便所有超链接都显示文本“管理”,但每个链接将相应的 UserName 值传入查询字符串。

将 HyperLinkField 添加到 GridView 后,请花点时间通过浏览器查看 ManageUsers.aspx 页面。 如图 1 所示,每个 GridView 行现在都包含一个“管理”链接。 Bruce 的“管理”链接指向 UserInformation.aspx?user=Bruce,而 Dave 的“管理”链接指向 UserInformation.aspx?user=Dave

HyperLinkField 添加

图 1:HyperLinkField 为每个用户帐户添加“管理”链接 (单击以查看全尺寸图像)

我们将稍后为 UserInformation.aspx 页面创建用户界面和代码,但首先让我们讨论如何以编程方式更改用户的锁定状态和已批准状态。 MembershipUser具有 IsLockedOutIsApproved 属性IsLockedOut 属性为只读。 没有以编程方式锁定用户的机制;若要解锁用户,请使用 MembershipUser 类的 UnlockUser 方法。 属性 IsApproved 是可读和可写的。 若要保存对此属性所做的任何更改,我们需要调用 Membership 类的 UpdateUser 方法,并传入修改 MembershipUser 后的 对象。

由于 属性 IsApproved 是可读和可写的,因此 CheckBox 控件可能是配置此属性的最佳用户界面元素。 但是,CheckBox 不适用于 属性, IsLockedOut 因为管理员无法锁定用户,她只能解锁用户。 属性的 IsLockedOut 合适用户界面是按钮,单击该按钮可解锁用户帐户。 仅当用户被锁定时,才应启用此按钮。

UserInformation.aspx创建页面

我们现在已准备好在 中 UserInformation.aspx实现用户界面。 打开此页并添加以下 Web 控件:

  • HyperLink 控件,单击该控件时,将管理员返回到页面 ManageUsers.aspx
  • 用于显示所选用户名的标签 Web 控件。 将此标签的 ID 设置为 UserNameLabel 并清除其 Text 属性。
  • 名为 的 IsApprovedCheckBox 控件。 将其 AutoPostBack 属性设置为 true
  • 用于显示用户上次锁定日期的 Label 控件。 将此标签 LastLockedOutDateLabel 命名为并清除其 Text 属性。
  • 用于解锁用户的按钮。 将此按钮 UnlockUserButton 命名为 ,并将其 Text 属性设置为“解锁用户”。
  • 用于显示状态消息的标签控件,例如“用户的已批准状态已更新”。将此控件 StatusMessage命名为 ,清除其 Text 属性,并将其 属性设置为 CssClassImportant。 (Important CSS 类在 Styles.css 样式表文件中定义;它以红色的大字体显示相应的文本。)

添加这些控件后,Visual Studio 中的“设计”视图应类似于图 2 中的屏幕截图。

为 UserInformation.aspx 创建用户界面

图 2:为 UserInformation.aspx (创建用户界面 单击以查看全尺寸图像)

用户界面完成后,下一个任务是基于所选用户的信息设置 IsApproved CheckBox 和其他控件。 为页面的事件 Load 创建事件处理程序,并添加以下代码:

protected void Page_Load(object sender, EventArgs e)
{
     if (!Page.IsPostBack)
     {

          // If querystring value is missing, send the user to ManageUsers.aspx
          string userName = Request.QueryString["user"];
          if (string.IsNullOrEmpty(userName))
               Response.Redirect("ManageUsers.aspx");

          // Get information about this user
          MembershipUser usr = Membership.GetUser(userName);
          if (usr == null)
               Response.Redirect("ManageUsers.aspx");

          UserNameLabel.Text = usr.UserName;
          IsApproved.Checked = usr.IsApproved;
          if (usr.LastLockoutDate.Year < 2000)

               LastLockoutDateLabel.Text = string.Empty;
          else
               LastLockoutDateLabel.Text = usr.LastLockoutDate.ToShortDateString();

          UnlockUserButton.Enabled = usr.IsLockedOut;
     }
}

上述代码首先确保这是第一次访问页面,而不是后续回发。 然后,它会读取通过查询字符串字段传递的 user 用户名,并通过 Membership.GetUser(username) 方法检索有关该用户帐户的信息。 如果未通过 querystring 提供用户名,或者找不到指定的用户,则会将管理员发送回页面 ManageUsers.aspx

然后,对象的MembershipUserUserName值会显示在 中UserNameLabelIsApproved并根据属性值检查 IsApproved CheckBox。

对象的 MembershipUserLastLockoutDate 属性 返回一个 DateTime 值,该值指示用户上次被锁定时间。如果用户从未被锁定,则返回的值取决于成员资格提供程序。 创建新帐户时, 会将SqlMembershipProvideraspnet_Membership表的 LastLockoutDate 字段设置为 1754-01-01 12:00:00 AM。 如果LastLockoutDate属性发生在 2000 年之前,则上述代码在 中LastLockoutDateLabel显示一个空字符串;否则,属性的LastLockoutDate日期部分显示在 Label 中。 s UnlockUserButton'Enabled 属性设置为用户的锁定状态,这意味着仅当用户被锁定时才启用此按钮。

花点时间通过浏览器测试 UserInformation.aspx 页面。 当然,需要从 ManageUsers.aspx 开始,然后选择要管理的用户帐户。 到达 UserInformation.aspx后,请注意, IsApproved 仅当用户获得批准时,才会检查 CheckBox。 如果用户曾经被锁定,则显示其上次锁定日期。 仅当用户当前被锁定时,才会启用“解锁用户”按钮。选中或取消选中 IsApproved CheckBox 或单击“解锁用户”按钮会导致回发,但不会对用户帐户进行任何修改,因为我们尚未为这些事件创建事件处理程序。

返回到 Visual Studio 并为 CheckBox 的 CheckedChanged 事件和 UnlockUser Button 的事件Click创建事件处理程序IsApproved。 在 CheckedChanged 事件处理程序中,将用户的 IsApproved 属性设置为 Checked CheckBox 的 属性,然后通过调用 Membership.UpdateUser保存更改。 在 事件处理程序中 Click ,只需调用 MembershipUser 对象的 UnlockUser 方法。 在这两个事件处理程序中,在 Label 中 StatusMessage 显示合适的消息。

protected void IsApproved_CheckedChanged(object sender, EventArgs e)
{
     // Toggle the user's approved status
     string userName = Request.QueryString["user"];
     MembershipUser usr = Membership.GetUser(userName);
     usr.IsApproved = IsApproved.Checked;
     Membership.UpdateUser(usr);
     StatusMessage.Text = "The user's approved status has been updated.";
}

protected void UnlockUserButton_Click(object sender, EventArgs e)
{
     // Unlock the user account
     string userName = Request.QueryString["user"];
     MembershipUser usr = Membership.GetUser(userName);

     usr.UnlockUser();
     UnlockUserButton.Enabled = false;
     StatusMessage.Text = "The user account has been unlocked.";
}

UserInformation.aspx测试页面

使用这些事件处理程序后,重新访问页面并取消批准用户。 如图 3 所示,应在页面上看到一条简短消息,指示已成功修改用户的 IsApproved 属性。

Chris 已被批准

图 3:Chris 已被批准 (单击以查看全尺寸图像)

接下来,注销并尝试以帐户未批准的用户身份登录。 由于用户未获批准,因此他们无法登录。 默认情况下,无论原因如何,如果用户无法登录,Login 控件都会显示相同的消息。 但在针对成员资格用户存储验证用户凭据教程中,我们查看了如何增强登录控件以显示更合适的消息。 如图 4 所示,Chris 会显示一条消息,说明他无法登录,因为他的帐户尚未获得批准。

Chris 无法登录,因为他的帐户未经批准

图 4:Chris 无法登录,因为他的帐户未经批准 (单击以查看全尺寸图像)

若要测试锁定功能,请尝试以批准的用户身份登录,但使用的密码不正确。 重复此过程所需的次数,直到用户帐户被锁定为止。如果尝试从锁定的帐户登录,则登录控件也已更新,以显示自定义消息。 一旦开始在登录页上看到以下消息,就知道帐户已被锁定:“由于无效登录尝试次数过多,帐户已被锁定。 请联系管理员以解锁你的帐户。”

返回到页面, ManageUsers.aspx 单击锁定用户的“管理”链接。 如图 5 所示,应该会在“解锁用户”按钮中看到 LastLockedOutDateLabel 一个值。 单击“解锁用户”按钮以解锁用户帐户。 解锁用户后,他们将能够再次登录。

戴夫已被锁定在系统外

图 5:Dave 已锁定系统 (单击以查看全尺寸图像)

步骤 2:指定新用户的已批准状态

如果希望在新用户能够登录并访问网站的用户特定功能之前执行某些操作,则“已批准”状态非常有用。 例如,你可能正在运行一个专用网站,其中除登录和注册页之外的所有页面都只能由经过身份验证的用户访问。 但是,如果陌生人访问您的网站、找到注册页面并创建帐户,会发生什么情况? 若要防止这种情况发生,可以将注册页移动到文件夹 Administration ,并要求管理员手动创建每个帐户。 或者,可以允许任何人注册,但在管理员批准用户帐户之前禁止站点访问。

默认情况下,CreateUserWizard 控件批准新帐户。 可以使用 控件的 DisableCreatedUser 属性配置此行为。 将此属性设置为 true 不批准新用户帐户。

注意

默认情况下,CreateUserWizard 控件会自动在新用户帐户上登录。 此行为由 控件的 LoginCreatedUser 属性决定。 由于未经批准的用户无法登录到站点,因此无论 属性的值LoginCreatedUser如何,当 为 trueDisableCreatedUser,新用户帐户都不会登录到站点。

如果要通过 Membership.CreateUser 方法以编程方式创建新用户帐户,若要创建未经批准的用户帐户,请使用接受新用户的属性值作为输入参数的 IsApproved 重载之一。

步骤 3:通过验证用户Email地址来批准用户

许多支持用户帐户的网站在验证注册时提供的电子邮件地址之前,不会批准新用户。 此验证过程通常用于阻止机器人、垃圾邮件发送者和其他非特定操作,因为它需要唯一且经过验证的电子邮件地址,并在注册过程中添加额外的步骤。 使用此模型,当新用户注册时,他们会收到一封电子邮件,其中包含指向验证页面的链接。 通过访问链接,用户已证明他们收到了电子邮件,因此提供的电子邮件地址是有效的。 验证页负责批准用户。 这可能会自动发生,从而批准访问此页面的任何用户,或仅在用户提供一些附加信息(如 CAPTCHA)之后。

为了适应此工作流,我们需要首先更新帐户创建页面,以便新用户未被批准。 EnhancedCreateUserWizard.aspx打开 文件夹中的页面Membership,并将 CreateUserWizard 控件的 DisableCreatedUser 属性设置为 true

接下来,我们需要配置 CreateUserWizard 控件,以便向新用户发送电子邮件,其中包含有关如何验证其帐户的说明。 具体而言,我们将在电子邮件 Verification.aspx 中包含指向页面 (的链接,该链接尚未创建) ,通过 querystring 传入新用户的 UserId 。 该 Verification.aspx 页面将查找指定的用户并将其标记为已批准。

向新用户发送验证Email

若要从 CreateUserWizard 控件发送电子邮件,请相应地配置其 MailDefinition 属性。 如上教程中所述,ChangePassword 和 PasswordRecovery 控件包含一个MailDefinition属性,该属性的工作方式与 CreateUserWizard 控件的工作方式相同。

注意

若要使用 属性, MailDefinition 需要在 中 Web.config指定邮件传递选项。 有关详细信息,请参阅在 ASP.NET 中发送Email

首先在 文件夹中创建名为 CreateUserWizard.txtEmailTemplates 的新电子邮件模板。 对模板使用以下文本:

Hello <%UserName%>! Welcome aboard.

Your new account is almost ready, but before you can login you must first visit:
<%VerificationUrl%>

Once you have visited the verification URL you will be redirected to the login page.

If you have any problems or questions, please reply to this email.

Thanks!

MailDefinition' s BodyFileName 属性设置为“~/EmailTemplates/CreateUserWizard.txt”,将其 Subject 属性设置为“欢迎访问我的网站! 请激活你的帐户。”

请注意, CreateUserWizard.txt 电子邮件模板包含占 <%VerificationUrl%> 位符。 这是页面 URL Verification.aspx 的放置位置。 CreateUserWizard 会自动将 <%UserName%><%Password%> 占位符替换为新帐户的用户名和密码,但没有内置 <%VerificationUrl%> 占位符。 我们需要将其手动替换为相应的验证 URL。

为此,请为 CreateUserWizard SendingMail 事件创建事件处理程序,并添加以下代码:

protected void NewUserWizard_SendingMail(object sender, MailMessageEventArgs e)
{
     // Get the UserId of the just-added user
     MembershipUser newUser = Membership.GetUser(NewUserWizard.UserName);

     Guid newUserId = (Guid)newUser.ProviderUserKey;

     // Determine the full verification URL (i.e., http://yoursite.com/Verification.aspx?ID=...)
     string urlBase = Request.Url.GetLeftPart(UriPartial.Authority) + 
          Request.ApplicationPath;

     string verifyUrl = "/Verification.aspx?ID=" + newUserId.ToString();
     string fullUrl = urlBase + verifyUrl;

     // Replace <%VerificationUrl%> with the appropriate URL and querystring
     e.Message.Body = e.Message.Body.Replace("<%VerificationUrl%>", fullUrl);
}

事件 SendingMail 在 事件后 CreatedUser 触发,这意味着,在上述事件处理程序执行时,已创建新的用户帐户。 可以通过调用 Membership.GetUser 方法访问新用户UserId的值,并将输入的 UserName 传入 CreateUserWizard 控件。 接下来,形成验证 URL。 语句 Request.Url.GetLeftPart(UriPartial.Authority) 返回 http://yourserver.com URL 的部分; Request.ApplicationPath 返回应用程序根目录的路径。 然后,验证 URL 定义为 Verification.aspx?ID=userId。 然后连接这两个字符串以形成完整的 URL。 最后,电子邮件正文 (e.Message.Body) 的所有匹配 <%VerificationUrl%> 项替换为完整的 URL。

其净效果是新用户未获批准,这意味着他们无法登录网站。 此外,系统会自动向其发送一封电子邮件,其中包含指向验证 URL 的链接 (见图 6) 。

新用户接收包含验证 URL 链接的Email

图 6:新用户接收包含验证 URL 链接的Email (单击以查看全尺寸图像)

注意

CreateUserWizard 控件的默认 CreateUserWizard 步骤显示一条消息,通知用户其帐户已创建,并显示“继续”按钮。 单击此项会将用户转到由 控件的 ContinueDestinationPageUrl 属性指定的 URL。 中的 EnhancedCreateUserWizard.aspx CreateUserWizard 配置为将新用户发送到 ~/Membership/AdditionalUserInfo.aspx,这会提示用户输入其家乡、主页 URL 和签名。 由于此信息只能由登录用户添加,因此更新此属性以将用户发送回网站的主页 (~/Default.aspx) 是有意义的。 此外, EnhancedCreateUserWizard.aspx 应扩充页面或 CreateUserWizard 步骤,以通知用户他们已收到验证电子邮件,在用户按照此电子邮件中的说明操作之前,不会激活其帐户。 我将这些修改留给读者练习。

创建验证页

最后一项任务是创建 Verification.aspx 页面。 将此页添加到根文件夹,将其与 Site.master 母版页相关联。 与之前添加到网站的大多数内容页一样,请删除引用 LoginContent ContentPlaceHolder 的 Content 控件,以便内容页使用母版页的默认内容。

向页面添加标签 Web 控件 Verification.aspx ,将其 ID 设置为 StatusMessage 并清除其文本属性。 接下来,创建 Page_Load 事件处理程序并添加以下代码:

protected void Page_Load(object sender, EventArgs e)
{
     if (string.IsNullOrEmpty(Request.QueryString["ID"]))
          StatusMessage.Text = "The UserId was not included in the querystring...";
     else
     {
          Guid userId;
          try
          {
               userId = new Guid(Request.QueryString["ID"]);
          }

          catch
          {
               StatusMessage.Text = "The UserId passed into the querystring is not in the
                    proper format...";
               return;
          }

          MembershipUser usr = Membership.GetUser(userId);
          if (usr == null)
               StatusMessage.Text = "User account could not be found...";
          else
          {
               // Approve the user
               usr.IsApproved = true;

               Membership.UpdateUser(usr);
               StatusMessage.Text = "Your account has been approved. 
                    Please <a href=\"Login.aspx\">login</a> to the site.";
          }
     }
}

上述代码的大部分验证 UserId 通过 querystring 提供的 是否存在、它是有效的 Guid 值以及它是否引用了现有的用户帐户。 如果所有这些检查都通过,则用户帐户获得批准;否则,将显示合适的状态消息。

图 7 显示了 Verification.aspx 通过浏览器访问时的页面。

新用户的帐户现已获得批准

图 7:新用户的帐户现已获得批准 (单击以查看全尺寸图像)

摘要

所有成员资格用户帐户都有两种状态,用于确定用户是否可以登录到站点: IsLockedOutIsApproved。 这两个属性都必须是 true ,用户才能登录。

用户的锁定状态用作安全措施,以减少黑客通过暴力破解方法闯入网站的可能性。 具体而言,如果在某个时间范围内有一定数量的无效登录尝试,用户将被锁定。 这些边界可通过 中的 Web.config成员资格提供程序设置进行配置。

批准状态通常用于禁止新用户登录,直到发生某些操作。 也许网站要求管理员首先批准新帐户,或者,正如我们在步骤 3 中看到的那样,通过验证其电子邮件地址来批准新帐户。

编程愉快!

关于作者

Scott Mitchell 是多本 ASP/ASP.NET 书籍的作者,4GuysFromRolla.com 的创始人,自 1998 年以来一直从事 Microsoft Web 技术工作。 Scott 担任独立顾问、培训师和作家。 他的最新书是 山姆斯在24小时内 ASP.NET 2.0自学。 可在 上或通过他的博客http://ScottOnWriting.NET联系 mitchell@4guysfromrolla.com Scott。

特别感谢...

本教程系列由许多有用的审阅者查看。 有兴趣查看我即将发布的 MSDN 文章? 如果是这样,请在 mitchell@4GuysFromRolla.com