恢复和更改密码 (VB)

作者 :斯科特·米切尔

注意

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

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

ASP.NET 包括两个 Web 控件,用于帮助恢复和更改密码。 PasswordRecovery 控件使访问者能够恢复丢失的密码。 ChangePassword 控件允许用户更新其密码。 与本系列教程中介绍的其他与登录相关的 Web 控件一样,PasswordRecovery 和 ChangePassword 控件在后台使用成员身份框架来重置或修改用户的密码。

简介

在我银行、公用事业公司、电话公司、电子邮件帐户和个性化 Web 门户的网站之间,我和大多数人一样,有数十个不同的密码要记住。 使用这些天来记住这么多凭据,人们忘记密码并不少见。 要考虑到这一点,提供用户帐户的网站需要包含一种方法,以便用户恢复其密码。 此过程通常涉及生成新的随机密码并将其发送到文件中用户的电子邮件地址。 收到新密码后,大多数用户返回到站点,并将密码从随机生成的密码更改为更难忘的密码。

ASP.NET 包括两个 Web 控件,用于帮助恢复和更改密码。 PasswordRecovery 控件使访问者能够恢复丢失的密码。 ChangePassword 控件允许用户更新其密码。 与本系列教程中介绍的其他与登录相关的 Web 控件一样,PasswordRecovery 和 ChangePassword 控件在后台使用成员身份框架来重置或修改用户的密码。

在本教程中,我们将使用这两个控件进行检查。 我们还将了解如何通过 MembershipUserChangePasswordResetPassword 方法以编程方式更改和重置用户的密码。

步骤 1:帮助用户恢复丢失的密码

支持用户帐户的所有网站都需要为用户提供恢复忘记的密码的一些机制。 好消息是,由于 PasswordRecovery Web 控件,在 ASP.NET 中实现此类功能是微不足道的。 PasswordRecovery 控件呈现一个界面,提示用户输入其用户名,并根据需要回答其安全问题。 然后,它会向用户发送电子邮件他们的密码。

注意

由于电子邮件通过纯文本通过网络传输,因此通过电子邮件发送用户密码存在安全风险。

PasswordRecovery 控件由三个视图组成:

  • UserName - 提示访问者输入其用户名。 这是初始视图。
  • 问题 - 将用户的用户名和安全问题显示为文本,以及用于用户输入其安全问题答案的 TextBox。
  • 成功 - 显示一条消息,告知用户其密码已通过电子邮件发送。

PasswordRecovery 控件显示的视图和操作取决于以下成员身份配置设置:

  • RequiresQuestionAndAnswer
  • EnablePasswordRetrieval
  • EnablePasswordReset

成员资格框架 RequiresQuestionAndAnswer 的设置指示用户在注册帐户时是否必须指定安全问题和答案。 正如我们在“创建用户帐户”教程中所述,如果RequiresQuestionAndAnswer为 True(默认值),则 CreateUserWizard 的界面包含新用户安全问题和答案的 TextBox 控件;如果RequiresQuestionAndAnswer为 False,则不会收集此类信息。 同样,如果 RequiresQuestionAndAnswer 为 True,则 PasswordRecovery 控件会在用户输入用户名后显示问题视图;仅当用户输入正确的安全答案时才会恢复密码。 但是,如果 RequiresQuestionAndAnswer 为 False,则 PasswordRecovery 控件将直接从“UserName”视图移动到“成功”视图。

在用户提供用户名或其用户名和安全答案后,如果 RequiresQuestionAndAnswer 为 True,则 PasswordRecovery 会向用户发送电子邮件,并发送其密码。 EnablePasswordRetrieval如果此选项设置为 True,则用户将通过电子邮件发送其当前密码。 如果它设置为 False 且 EnablePasswordReset 设置为 True,则 PasswordRecovery 控件将为用户生成一个新的随机密码,并向用户发送电子邮件此新密码。 如果同时为 EnablePasswordRetrieval EnablePasswordReset False,则 PasswordRecovery 控件将引发异常。

注意

回想一下,将 SqlMembershipProvider 用户的密码存储为以下三种格式之一:Clear、Hashed(默认值)或 Encrypted。 使用的存储机制取决于成员资格配置设置;演示应用程序使用哈希密码格式。 使用哈希密码格式时, EnablePasswordRetrieval 必须将选项设置为 False,因为系统无法从数据库中存储的哈希版本确定用户的实际密码。

图 1 说明了 PasswordRecovery 的接口和行为如何受到成员资格配置的影响。

RequiresQuestionAndAnswer、EnablePasswordRetrieval 和 EnablePasswordReset 影响 PasswordRecovery 控件的外观和行为

图 1RequiresQuestionAndAnswerEnablePasswordRetrievalPasswordRecovery EnablePasswordReset 控件的外观和行为(单击以查看全尺寸图像

注意

SQL Server 中创建成员资格架构教程中,我们通过将成员资格提供程序设置为 RequiresQuestionAndAnswer True、EnablePasswordRetrieval设置为 False 和 EnablePasswordReset True 来配置成员资格提供程序。

使用 PasswordRecovery 控件

让我们看看在 ASP.NET 页面中使用 PasswordRecovery 控件。 打开 RecoverPassword.aspx 并拖放工具箱中的 PasswordRecovery 控件到设计器;将其 ID 设置为 RecoverPwd。 与 Login 和 CreateUserWizard Web 控件一样,PasswordRecovery 控件的视图呈现一个丰富的复合界面,其中包括标签、TextBoxes、按钮和验证控件。 可以通过控件的样式属性或通过将视图转换为模板来自定义视图的外观。 我离开这个作为一个练习,为感兴趣的读者。

当用户访问此页面时,她将输入用户名并单击“提交”按钮。 由于我们在成员身份配置设置中已将 RequiresQuestionAndAnswer 属性设置为 True,因此 PasswordRecovery 控件将显示问题视图。 用户输入正确的安全答案并单击“提交”后,PasswordRecovery 控件会将用户的密码更新为随机生成的密码,并将此密码发送到文件中的电子邮件地址。 所有这些操作都是可能的,无需我们编写一行代码!

在测试此页面之前,有一个最终的配置需要:我们需要在其中 Web.config指定邮件传递设置。 PasswordRecovery 控件依赖于这些设置来发送电子邮件。

邮件传递配置是通过 <system.net> 元素<mailSettings> 元素指定的。 使用 <smtp> 元素 指示传递方法和默认 From 地址。 以下标记将邮件设置配置为使用端口 25 上命名 smtp.example.com 的网络 SMTP 服务器,以及用户名和密码的用户名/密码凭据。

注意

<system.net> 是根 <configuration> 元素和同级元素的 <system.web>子元素。 因此,不要将 <system.net> 元素放在元素中 <system.web> ;而是将其置于同一级别。

除了在网络上使用 SMTP 服务器之外,还可以指定要发送的电子邮件的取件目录。

配置 SMTP 设置后,通过浏览器访问 RecoverPassword.aspx 页面。 首先尝试输入用户存储中不存在的用户名。 如图 2 所示,PasswordRecovery 控件显示一条消息,指示无法访问用户信息。 可以通过控件 UserNameFailureText 的属性自定义消息的文本。

如果输入了无效用户名,则会显示错误消息

图 2:如果输入了无效用户名,将显示错误消息(单击以查看全尺寸图像

现在输入用户名。 将系统中帐户的用户名与可以访问的电子邮件地址以及你知道的安全答案一起使用。 输入用户名并单击“提交”后,PasswordRecovery 控件将显示其问题视图。 与“用户名”视图一样,如果输入错误的答案,PasswordRecovery 控件将显示错误消息(请参阅图 3)。 使用属性QuestionFailureText自定义此错误消息。

如果用户输入了无效的安全答案,则会显示错误消息

图 3:如果用户输入无效的安全答案,将显示错误消息(单击以查看全尺寸图像

最后,输入正确的安全答案,然后单击“提交”。 在后台,PasswordRecovery 控件会生成一个随机密码,将其分配给用户帐户,发送电子邮件通知用户其新密码(请参阅图 4),然后显示“成功”视图。

用户发送了一封电子邮件,其中包含他的新密码

图 4:用户发送了一封电子邮件,其中包含他的新密码(单击以查看全尺寸图像

自定义电子邮件

PasswordRecovery 控件发送的默认电子邮件相当沉闷(见图 4)。 该消息通过使用者密码和纯文本正文的元素from属性中指定的<smtp>帐户发送:

请使用以下信息返回到站点并登录。

用户名: 用户名

密码: 密码

可以通过 PasswordRecovery 控件事件的SendingMail事件处理程序以编程方式自定义此消息,也可以通过属性以声明方式自定义。MailDefinition 让我们探讨这两个选项。

SendingMail 发送电子邮件之前立即触发该事件,也是我们最后一次以编程方式调整电子邮件的机会。 引发此事件时,事件处理程序将传递一个类型 MailMessageEventArgs的对象,其 Message 属性包含对即将发送的电子邮件的引用。

SendingMail 事件创建事件处理程序并添加以下代码,该代码以编程方式添加到 webmaster@example.com CC 列表。

Protected Sub RecoverPwd_SendingMail(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.MailMessageEventArgs)Handles RecoverPwd.SendingMail
 e.Message.CC.Add("webmaster@example.com")
End Sub

还可以通过声明性方式配置电子邮件。 PasswordRecovery 的属性 MailDefinition 是一个类型 MailDefinition的对象。 该MailDefinition类提供许多与电子邮件相关的属性,包括FromCC、、PrioritySubjectIsBodyHtmlBodyFileName和其他属性。 对于初学者,请将属性Subject设置为比默认使用的属性更具描述性(密码),例如密码已重置...

若要自定义电子邮件正文,我们需要创建一个单独的包含正文内容的电子邮件模板文件。 首先,在名为 EmailTemplates“. 接下来,将一个新的文本文件添加到名为 PasswordRecovery.txt 此文件夹,并添加以下内容:

Your password has been reset, <%UserName%>!

According to our records, you have requested that your password be reset. Your new
password is: <%Password%>

If you have any questions or trouble logging on please contact a site administrator.

Thank you!

请注意占位符 <%UserName%> 的使用和 <%Password%>。 PasswordRecovery 控件会在发送电子邮件之前自动将这两个占位符替换为用户的用户名和恢复的密码。

最后,将 MailDefinition's BodyFileName 属性 指向刚刚创建的电子邮件模板(~/EmailTemplates/PasswordRecovery.txt)。

进行这些更改后,重新访问 RecoverPassword.aspx 页面并输入用户名和安全答案。 应收到类似于图 5 中电子邮件的电子邮件。 请注意, webmaster@example.com 已抄送和主题和正文已更新。

主题、正文和 CC 列表已更新

图 5:主题、正文和抄送列表已更新(单击以查看全尺寸图像

若要将 HTML 格式的电子邮件设置为 IsBodyHtml True(默认值为 False),并更新电子邮件模板以包含 HTML。

MailDefinition 属性对 PasswordRecovery 类不是唯一的。 如我们在步骤 2 中看到的那样,ChangePassword 控件还提供一个 MailDefinition 属性。 此外,CreateUserWizard 控件包含这样的属性,你可以配置为自动向新用户发送欢迎电子邮件。

注意

目前,左侧导航中没有用于访问 RecoverPassword.aspx 页面的链接。 如果用户无法成功登录网站,则仅对访问此页面感兴趣。 因此,更新 Login.aspx 页面以包含指向 RecoverPassword.aspx 页面的链接。

以编程方式重置用户的密码

重置用户的密码时,PasswordRecovery 控件将调用 MembershipUser 对象的 ResetPassword 方法。 此方法有两个重载:

  • ResetPassword - 重置用户的密码。 如果 RequiresQuestionAndAnswer 为 False,请使用此重载。
  • ResetPassword(securityAnswer) - 仅当提供的 securityAnswer 正确时,才重置用户的密码。 如果 RequiresQuestionAndAnswer 为 True,请使用此重载。

这两个重载都返回新的随机生成的密码。

与成员资格框架中的其他方法一样,该方法 ResetPassword 将委托给配置的提供程序。 aspnet_Membership_ResetPassword调用SqlMembershipProvider存储过程,传入用户的用户名、新密码和提供的密码答案以及其他字段。 存储过程可确保密码答案匹配,然后更新用户的密码。

几个低级别实现说明:

  • 锁定的用户无法重置其密码。 但是,未经批准的用户可能。 我们将在“解锁和批准用户帐户”教程中更详细地讨论锁定状态和已批准状态。
  • 如果密码答案不正确,则用户失败的密码应答尝试计数会递增。 如果在指定的时间范围内发生指定数量的无效安全应答尝试,则用户将被锁定。

关于如何生成随机密码的 Word

图 4 和 5 中电子邮件中显示的随机生成的密码由 Membership 类 GeneratePassword 的方法创建。 此方法接受两个整数输入参数 -length 和 numberOfNonAlphanumericCharacters - 并返回长度至少为长度的字符串,长度至少为 OfNonAlphanumericCharacters 数目的非字母数字字符。 从成员资格类或登录相关的 Web 控件中调用此方法时,这两个参数的值由成员资格配置 MinRequiredPasswordLengthMinRequiredNonalphanumericCharacters 属性(我们分别设置为 7 和 1)确定。

该方法 GeneratePassword 使用加密强随机数生成器来确保所选随机字符中没有偏差。 此外,也就是说PublicGeneratePassword如果需要生成随机字符串或密码,可以直接从 ASP.NET 应用程序使用它。

注意

SqlMembershipProvider 类始终生成长度至少为 14 个字符的随机密码,因此如果 MinRequiredPasswordLength 小于 14,则忽略其值。

步骤 2:更改密码

随机生成的密码难以记住。 请考虑图 4 中所示的密码。 WWGUZv(f2yM:Bd 请尝试将它提交到内存! 无需说,在向用户发送此类随机生成的密码后,她需要将密码更改为更令人难忘的内容。

使用 ChangePassword 控件为用户创建一个界面来更改其密码。 与 PasswordRecovery 控件非常类似,ChangePassword 控件包含两个视图:更改密码和成功。 “更改密码”视图提示用户输入其旧密码和新密码。 提供正确的旧密码和满足最小长度和非字母数字字符要求的新密码后,ChangePassword 控件将更新用户的密码并显示“成功”视图。

注意

ChangePassword 控件通过调用 MembershipUser 对象的 ChangePassword 方法来修改用户的密码。 ChangePassword 方法接受两个String输入参数 -oldPasswordnewPassword- 并使用 newPassword 更新用户帐户,假设提供的 oldPassword 正确。

ChangePassword.aspx打开页面,并向页面添加 ChangePassword 控件,将其ChangePwd命名。 此时,设计视图应显示“更改密码”视图(请参阅图 6)。 与 PasswordRecovery 控件一样,可以通过控件的智能标记在视图之间切换。 此外,这些视图的外观可以通过分类的样式属性或通过将它们转换为模板来自定义。

向页面添加 ChangePassword 控件

图 6:向页面添加 ChangePassword 控件(单击以查看全尺寸图像

ChangePassword 控件可以更新当前登录的用户密码或另一个指定用户的密码。 如图 6 所示,默认的“更改密码”视图只呈现三个 TextBox 控件:一个用于旧密码,两个用于新密码。 此默认接口用于更新当前登录的用户密码。

若要使用 ChangePassword 控件更新其他用户的密码,请将控件 DisplayUserName 的属性 设置为 True。 这样做会将第四个 TextBox 添加到页面,提示输入要更改其密码的用户的用户名。

如果要让已注销的用户更改其密码,而无需登录,则设置为 DisplayUserName True 非常有用。 就个人而言,我认为在允许她更改密码之前,要求用户登录没有任何问题。 因此,保留 DisplayUserName 设置为 False(默认值)。 然而,在做出此决定时,我们基本上禁止匿名用户访问此页面。 更新网站的 URL 授权规则,以拒绝匿名用户访问 ChangePassword.aspx。 如果需要刷新 URL 授权规则语法上的内存,请参阅 基于用户的授权 教程。

注意

该属性似乎 DisplayUserName 可用于允许管理员更改其他用户的密码。 但是,即使 DisplayUserName 设置为 True,正确的旧密码也必须已知并输入。 我们将讨论允许管理员在步骤 3 中更改用户密码的技术。

ChangePassword.aspx通过浏览器访问页面并更改密码。 请注意,如果输入不符合成员身份配置中指定的密码长度和非字母数字字符要求的新密码,则会显示错误消息(请参阅图 7)。

如果输入不符合密码长度和非字母数字字符要求的新密码,将显示一条错误消息。

图 7:向页面添加 ChangePassword 控件(单击以查看全尺寸图像

输入正确的旧密码和有效的新密码后,已登录用户的密码将更改,并显示“成功”视图。

发送确认电子邮件

默认情况下,ChangePassword 控件不会向刚刚更新其密码的用户发送电子邮件。 如果要发送电子邮件,只需配置控件 MailDefinition 的属性。 让我们配置 ChangePassword 控件,以便用户发送包含其新密码的 HTML 格式的电子邮件。

首先在EmailTemplates名为 <a0/a0> 的文件夹中创建新文件。 添加以下标记:

<html>
 <body>
 <h2>Your Password Has Been Changed!</h2>
 <p>
 This email confirms that your password has been changed.
 </p>
 <p>
 To log on to the site, use the following credentials:
 </p>
 <table>
 <tr>
 <td>
 <b>Username:</b>
 </td>
 <td>
 <%UserName%>
 </td>
 </tr>
 <tr>
 <td>
 <b>Password:</b>
 </td>
 <td>
 <%Password%>
 </td>
 </tr>
 </table>
 <p>
 If you have any questions or encounter any problems logging in,
 please contact a site administrator.
 </p>
 </body>
</html>

接下来,将 ChangePassword 控件MailDefinition的属性BodyFileNameIsBodyHtmlSubject属性分别设置为 ~/EmailTemplates/ChangePassword.htm、True 和 Your password!。

进行这些更改后,重新访问页面并再次更改密码。 这一次,ChangePassword 控件会将自定义的 HTML 格式的电子邮件发送到文件上的用户电子邮件地址(请参阅图 8)。

电子邮件通知用户其密码已更改

图 8:电子邮件通知用户其密码已更改(单击以查看全尺寸图像

步骤 3:允许管理员更改用户密码

支持用户帐户的应用程序中的一项常见功能是管理用户能够更改其他用户的密码。 有时,此功能是必需的,因为系统缺少用户更改自己的密码的能力。 在这种情况下,用户恢复忘记的密码的唯一方法是管理员为其分配新密码。 但是,借助 PasswordRecovery 和 ChangePassword 控件,管理用户无需忙于更改用户的密码,因为用户能够自行执行此操作。

但是,如果客户端坚持管理用户应该能够更改其他用户的密码,该怎么办? 遗憾的是,添加此功能可能有点工作。 若要更改用户的密码,必须向对象的ChangePassword方法提供MembershipUser旧密码和新密码,但管理员不必知道用户的密码才能对其进行修改。

一种解决方法是先重置用户的密码,然后使用如下所示的代码将其更改为新密码:

Dim usr As MembershipUser = Membership.GetUser(username)
Dim resetPwd As String = usr.ResetPassword()
usr.ChangePassword(resetPwd, newPassword)

此代码首先检索有关 用户名的信息,即管理员希望更改其密码的用户。 接下来, ResetPassword 将调用该方法,该方法将分配和用户一个新的随机密码。 此方法返回此随机生成的密码,并存储在变量 resetPwd中。 现在,我们知道用户的密码,我们可以通过调用更改 ChangePassword它。

问题是,仅当成员身份系统配置设置为 RequiresQuestionAndAnswer False 时,此代码才有效。 如果 RequiresQuestionAndAnswer 为 True,就像应用程序一样,则 ResetPassword 方法需要传递安全答案,否则将引发异常。

如果成员资格框架配置为需要安全问题和答案,但客户端坚持认为管理员能够更改用户的密码,你有三个选项:

  • 把手扔在空中,告诉客户,这只是一件事,无法完成。
  • 设置为 RequiresQuestionAndAnswer False。 这会导致不太安全的应用程序。 假设恶意用户已获得其他用户的电子邮件收件箱的访问权限。 也许被入侵的用户已经离开办公桌去午餐,没有锁定他们的工作站,或者他们从公共终端访问了他们的电子邮件,也没有注销。在任一情况下,恶意用户都可以访问 RecoverPassword.aspx 页面并输入用户的用户名。 然后,系统会通过电子邮件发送恢复的密码,而不会提示提供安全答案。
  • 绕过成员资格框架创建的抽象层,并直接使用 SQL Server 数据库。 成员身份架构包括一 aspnet_Membership_SetPassword 个名为设置用户密码且不需要安全答案或旧密码才能完成任务的存储过程。

这些选项都不特别吸引人,但这就是开发人员的一生有时如何。

我继续执行第三种方法,编写代码,绕过 MembershipMembershipUser 类并直接针对 SecurityTutorials 数据库操作。

注意

直接使用数据库时,成员身份框架提供的封装会破裂。 此决定将我们与代码联系在一 SqlMembershipProvider起,使代码不那么可移植。 此外,如果成员资格架构发生更改,此代码在 ASP.NET 的未来版本中可能无法按预期工作。 此方法是一种解决方法,与大多数解决方法一样,不是最佳做法的示例。

代码具有一些无吸引力位,并且相当长。 因此,我不想对本教程进行深入的检查。 如果有兴趣了解详细信息,请下载本教程的代码并访问 ~/Administration/ManageUsers.aspx 该页。 在前面的教程中创建的此页面列出了每个用户。 我已更新 GridView 以包含指向 UserInformation.aspx 页面的链接,并通过 querystring 传递所选用户的用户名。 该 UserInformation.aspx 页显示有关所选用户和 TextBoxes 更改其密码的信息(请参阅图 9)。

输入新密码后,在第二个 TextBox 中确认它,然后单击“更新用户按钮”,随后会调用回发和 aspnet_Membership_SetPassword 存储过程,更新用户的密码。 我鼓励那些对此功能感兴趣的读者更熟悉代码,并尝试扩展功能以包括向更改了密码的用户发送电子邮件。

管理员可能会更改用户的密码

图 9:管理员可能会更改用户的密码(单击以查看全尺寸图像

注意

仅当成员资格框架配置为以清除或哈希格式存储密码时,页面 UserInformation.aspx 当前才有效。 它缺少用于加密新密码的代码,尽管你受邀添加此功能。 建议添加所需代码的方式是使用反编译程序(如反射器)检查 .NET Framework 中方法的ChangePassword源代码;首先检查SqlMembershipProvider类的方法。 这是用于编写用于创建密码哈希的代码的技术。

总结

ASP.NET 提供两个控件来帮助用户管理其密码。 PasswordRecovery 控件对于忘记密码的用户非常有用。 根据成员资格框架的配置,用户将通过电子邮件发送其现有密码或新的随机生成的密码。 ChangePassword 控件使用户能够更新其密码。

与 Login 和 CreateUserWizard 控件一样,PasswordRecovery 和 ChangePassword 控件呈现丰富的用户界面,而无需编写声明性标记或代码行。 如果默认用户界面不满足你的需求,可以通过各种样式属性对其进行自定义。 或者,控件的接口可以转换为模板,以便更精细地控制。 这些控件在后台使用成员身份 API,调用 MembershipUser 对象的 ResetPassword 方法和 ChangePassword 方法。

快乐编程!

深入阅读

有关本教程中讨论的主题的详细信息,请参阅以下资源:

关于作者

斯科特·米切尔是多个 ASP/ASP.NET 书籍的作者,4GuysFromRolla.com 的创始人,自1998年以来一直在与Microsoft Web 技术合作。 斯科特担任独立顾问、教练和作家。 他的最新书是 山姆斯在24小时内 ASP.NET 2.0。 斯科特可以在他的博客上 mitchell@4guysfromrolla.com 或通过他的博客联系 http://ScottOnWriting.NET

特别感谢

本教程系列由许多有用的审阅者审阅。 本教程的主要审阅者包括 Michael Emmings 和 Suchi Banerjee。 有兴趣查看即将发布的 MSDN 文章? 如果是这样,请在以下位置放置我一行 mitchell@4GuysFromRolla.com