创建用户帐户 (VB)

作者 :斯科特·米切尔

注意

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

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

下载代码下载 PDF

在本教程中,我们将探讨如何使用成员身份框架(通过 SqlMembershipProvider)创建新的用户帐户。 我们将了解如何以编程方式和通过 ASP 创建新用户。NET 的内置 CreateUserWizard 控件。

简介

前面的教程中,我们在数据库中安装了应用程序服务架构,该架构添加了该数据库所需的SqlMembershipProviderSqlRoleProvider表、视图和存储过程。 这将创建本系列教程的其余部分所需的基础结构。 在本教程中,我们将探讨如何使用成员资格框架(通过 SqlMembershipProvider)创建新的用户帐户。 我们将了解如何以编程方式和通过 ASP 创建新用户。NET 的内置 CreateUserWizard 控件。

除了了解如何创建新用户帐户之外,还需要更新我们在“表单身份验证概述”教程中创建的演示网站。 我们的演示 Web 应用程序有一个登录页,它根据硬编码的用户名/密码对验证用户的凭据。 此外, Global.asax 还包括为经过身份验证的用户创建自定义 IPrincipalIIdentity 对象的代码。 我们将更新登录页,以根据成员身份框架验证用户的凭据,并删除自定义主体和标识逻辑。

现在就开始吧!

表单身份验证和成员资格清单

在开始使用成员资格框架之前,让我们花点时间查看我们为此而采取的重要步骤。 在基于表单的身份验证方案中将成员资格框架用于 SqlMembershipProvider 时,需要在 Web 应用程序中实现成员身份功能之前执行以下步骤:

  1. 启用基于表单的身份验证。 正如我们在窗体身份验证概述中讨论的那样,通过编辑Web.config和设置<authentication>元素mode的属性来Forms启用表单身份验证。 启用表单身份验证后,会检查每个传入请求的 表单身份验证票证(如果存在)标识请求者。
  2. 将应用程序服务架构添加到相应的数据库。 使用 SqlMembershipProvider 时,我们需要将应用程序服务架构安装到数据库。 通常,此架构将添加到保存应用程序数据模型的同一数据库。 在 SQL Server 中创建成员身份架构教程介绍了如何使用 aspnet_regsql.exe 该工具来完成此操作。
  3. 自定义 Web 应用程序的设置以引用步骤 2 中的数据库。 SQL Server 中的“创建成员身份架构”教程演示了两种方法来配置 Web 应用程序,以便SqlMembershipProvider使用步骤 2 中选择的数据库:修改LocalSqlServer连接字符串名称;或者将新的注册提供程序添加到成员身份框架提供程序列表中,并自定义该新提供程序以使用步骤 2 中的数据库。

生成使用基于表单的 SqlMembershipProvider 身份验证的 Web 应用程序时,在使用类或 ASP.NET 登录 Web 控件之前 Membership ,需要执行这三个步骤。 由于已在前面的教程中执行这些步骤,因此我们已准备好开始使用成员资格框架!

步骤 1:添加新 ASP.NET 页面

在本教程和接下来的三个教程中,我们将检查各种与成员身份相关的函数和功能。 我们需要一系列 ASP.NET 页面来实现这些教程中审查的主题。 让我们创建这些页面,然后创建一个网站地图文件 (Web.sitemap)

首先在名为 <a0/a0> 的项目中创建一个新文件夹。 接下来,将五个新的 ASP.NET 页添加到 Membership 文件夹中,将每个页面与母版页链接在一起 Site.master 。 为页面命名:

  • CreatingUserAccounts.aspx
  • UserBasedAuthorization.aspx
  • EnhancedCreateUserWizard.aspx
  • AdditionalUserInfo.aspx
  • Guestbook.aspx

此时,项目的解决方案资源管理器应类似于图 1 中显示的屏幕截图。

已将五个新页面添加到成员身份文件夹

图 1:已向 Membership 文件夹添加五个新页面(单击以查看全尺寸图像

此时,每个页面都应有两个内容控件,一个用于母版页的 ContentPlaceHolders: MainContentLoginContent

<asp:Content ID="Content1" ContentPlaceHolderID="MainContent"
Runat="Server"> 
</asp:Content> 
<asp:Content ID="Content2" ContentPlaceHolderID="LoginContent"
Runat="Server"> 
</asp:Content>

回想一下, LoginContent ContentPlaceHolder 的默认标记显示登录或注销站点的链接,具体取决于用户是否经过身份验证。 但是,内容控件的存在 Content2 会替代母版页的默认标记。 正如我们在窗体身份验证概述教程中讨论的那样,这在不想在左侧列中显示与登录相关的选项的页面非常有用。

但是,对于这五个页面,我们希望显示 ContentPlaceHolder 的母版页的默认标记 LoginContent 。 因此,删除内容控件的 Content2 声明性标记。 执行此操作后,五页的标记中的每个标记应仅包含一个内容控件。

步骤 2:创建网站地图

除了最琐碎的网站,还需要实现某种形式的导航用户界面。 导航用户界面可以是指向网站各个部分的链接的简单列表。 或者,这些链接可以排列为菜单或树视图。 作为页面开发人员,创建导航用户界面只是故事的一半。 我们还需要一些方法来以可维护且可更新的方式定义网站的逻辑结构。 添加新页面或删除现有页面时,我们希望能够更新单个源(网站地图),并让这些修改反映在网站的导航用户界面中。

这两项任务(定义网站地图和基于网站地图实现导航用户界面)很容易完成,这要归功于网站地图框架和 ASP.NET 版本 2.0 中添加的导航 Web 控件。 网站地图框架允许开发人员定义网站地图,然后通过编程 API( SiteMap)访问它。 内置的导航 Web 控件包括 菜单控件TreeView 控件SiteMapPath 控件

与成员资格和角色框架一样,站点映射框架是在提供程序模型之上构建的。 站点映射提供程序类的作业是从持久性数据存储(如 XML 文件或数据库表)生成类使用的 SiteMap 内存中结构。 .NET Framework 附带一个默认站点地图提供程序,该提供程序从 XML 文件(XmlSiteMapProvider)读取站点地图数据,这是我们将在本教程中使用的提供程序。 有关一些备用站点地图提供程序实现,请参阅本教程末尾的“进一步阅读”部分。

默认站点地图提供程序需要一 Web.sitemap 个名为正确格式的 XML 文件存在于根目录。 由于我们使用此默认提供程序,因此需要添加此类文件,并使用适当的 XML 格式定义站点地图的结构。 若要添加文件,请右键单击解决方案资源管理器中的项目名称,然后选择“添加新项”。 从对话框中,选择添加名为 Web.sitemap“网站地图”类型的文件。

将名为 Web.sitemap 的文件添加到项目的根目录

图 2:添加名为 Web.sitemap 项目根目录的文件(单击以查看全尺寸图像

XML 站点映射文件将网站的结构定义为层次结构。 此分层关系通过元素的 <siteMapNode> 祖先在 XML 文件中建模。 必须Web.sitemap从具有一个<siteMap><siteMapNode>子节点的父节点开始。 此顶级 <siteMapNode> 元素表示层次结构的根,并且可能具有任意数量的后代节点。 每个 <siteMapNode> 元素必须包含一个 title 属性,并且可以选择性地包含 url 属性和 description 属性;每个非空 url 属性必须是唯一的。

在文件中输入以下 XML Web.sitemap

<?xml version="1.0" encoding="utf-8" ?> 
<siteMap xmlns="http://schemas.microsoft.com/AspNet/SiteMap-File-1.0"> 
 <siteMapNode url="~/Default.aspx" title="Home"> 
 <siteMapNode title="Membership">
 <siteMapNode url="~/Membership/CreatingUserAccounts.aspx" title="Creating User Accounts" /> 
 <siteMapNode url="~/Membership/UserBasedAuthorization.aspx" title="User-Based Authorization" /> 
 <siteMapNode url="~/Membership/Guestbook.aspx" title="Storing Additional User Information" /> 
 </siteMapNode> 
 </siteMapNode> 
</siteMap>

上面的网站地图标记定义图 3 中显示的层次结构。

站点地图表示分层导航结构

图 3:网站地图表示分层导航结构(单击以查看全尺寸图像

步骤 3:更新母版页以包含导航用户界面

ASP.NET 包括许多与导航相关的 Web 控件,用于设计用户界面。 其中包括 Menu、TreeView 和 SiteMapPath 控件。 Menu 和 TreeView 控件分别在菜单或树中呈现站点地图结构,而 SiteMapPath 会显示一个痕迹导航,显示正在访问的当前节点及其祖先。 网站地图数据可以使用 SiteMapDataSource 绑定到其他数据 Web 控件,并通过类以编程方式 SiteMap 访问。

由于对网站地图框架和导航控件的深入讨论超出了本教程系列的范围,而不是花时间创建我们自己的导航用户界面,让我们来借用我在 ASP.NET 2.0 教程系列中使用的数据,该系列使用 Repeater 控件来显示导航链接的两深项目符号列表, 如图 4 所示。

若要创建此接口,请将以下声明性标记添加到 Site.master 母版页的左侧列,其中文本 TODO:菜单将转到此处...当前驻留。

<ul> 
 <li> 
 <asp:HyperLink runat="server" ID="lnkHome" NavigateUrl="~/Default.aspx">Home</asp:HyperLink>
 </li> 
 <asp:Repeater runat="server" ID="menu" DataSourceID="SiteMapDataSource1"> 
 <ItemTemplate>
 <li> 
 <asp:HyperLink ID="lnkMenuItem" runat="server" 
 NavigateUrl='<%# Eval("Url") %>'><%# Eval("Title") %></asp:HyperLink>
 <asp:Repeater ID="submenu" runat="server" DataSource="<%# 
 CType(Container.DataItem, SiteMapNode).ChildNodes %>"> 
 <HeaderTemplate> 
 <ul> 
 </HeaderTemplate> 
 <ItemTemplate>
 <li> 
 <asp:HyperLink ID="lnkMenuItem" 
 runat="server" NavigateUrl='<%#  Eval("Url") %>'><%# Eval("Title") %></asp:HyperLink> 
 </li>
 </ItemTemplate> 
 <FooterTemplate> 
 </ul> 
 </FooterTemplate>
 </asp:Repeater> 
 </li> 
 </ItemTemplate> 
 </asp:Repeater> 
</ul>
<asp:SiteMapDataSource ID="SiteMapDataSource1" runat="server" ShowStartingNode="false"/>

上述标记将一个名为 menu SiteMapDataSource 的 Repeater 控件绑定,该控件返回在 中 Web.sitemap定义的站点地图层次结构。 由于 SiteMapDataSource 控件 ShowStartingNode 的属性 设置为 False,因此它开始返回站点地图的层次结构,从主节点的后代开始。 Repeater 在元素中 <li> 显示每个节点(当前只是成员身份)。 然后,内部 Repeater 在嵌套的无序列表中显示当前节点的子级。

图 4 显示了上述标记的呈现输出,其中包含我们在步骤 2 中创建的站点地图结构。 Repeater 呈现香草无序列表标记;定义的 Styles.css 级联样式表规则负责美观的布局。 有关上述标记工作原理的更详细说明,请参阅 母版页和网站导航 教程。

导航用户界面是使用嵌套的无序列表呈现的

图 4:使用嵌套的无序列表呈现导航用户界面(单击以查看全尺寸图像

添加痕迹导航

除了左侧列中的链接列表外,我们还让每个页面都显示痕迹 导航。 痕迹导航是导航用户界面元素,可快速显示用户在网站层次结构中的当前位置。 SiteMapPath 控件使用网站地图框架来确定当前页在网站地图中的位置,然后基于此信息显示痕迹导航。

具体而言,将 <span> 元素添加到母版页的标头 <div> 元素,并将新 <span> 元素 class 的属性设置为痕迹导航。 (该 Styles.css 类包含痕迹导航类的规则。)接下来,将 SiteMapPath 添加到此新 <span> 元素。

<div id="header"> 
 <span class="title">User Account Tutorials</span><br /> 
 <span class="breadcrumb"> 
 <asp:SiteMapPath ID="SiteMapPath1" runat="server"> 
 </asp:SiteMapPath> 
 </span> 
</div>

图 5 显示了访问 ~/Membership/CreatingUserAccounts.aspx时 SiteMapPath 的输出。

痕迹导航显示站点地图中的当前页及其上级

图 5:痕迹导航显示站点地图中的当前页及其上级(单击可查看全尺寸图像

步骤 4:删除自定义主体和标识逻辑

自定义主体和标识对象可以关联到经过身份验证的用户。 我们通过为应用程序的PostAuthenticateRequest事件创建事件处理程序来实现此目的,该事件处理程序Global.asaxFormsAuthenticationModule对用户进行身份验证后触发。 在此事件处理程序中,我们用在本教程中创建的对象替换了 GenericPrincipal FormsIdentity FormsAuthenticationModule 添加的对象 CustomPrincipalCustomIdentity 对象。

虽然自定义主体和标识对象在某些方案中很有用,但在大多数情况下, GenericPrincipal 并且 FormsIdentity 对象已足够。 因此,我认为返回默认行为是值得的。 通过删除或注释掉 PostAuthenticateRequest 事件处理程序或完全删除 Global.asax 文件来实现此更改。

注意

注释或删除代码Global.asax后,需要注释掉将属性CustomIdentity强制转换为User.Identity实例的代码隐藏类中的Default.aspx's代码。

步骤 5:以编程方式创建新用户

若要通过成员身份框架创建新的用户帐户,请使用 MembershipCreateUser 的方法。 此方法具有用户名、密码和其他用户相关字段的输入参数。 在调用时,它将新用户帐户的创建委托给配置的成员资格提供程序,然后返回一个 MembershipUser 表示刚刚创建的用户帐户的对象

该方法 CreateUser 有四个重载,每个重载接受不同数量的输入参数:

这四个重载因收集的信息量而异。 例如,第一个重载只需要新用户帐户的用户名和密码,而第二个重载还需要用户的电子邮件地址。

存在这些重载,因为创建新用户帐户所需的信息取决于成员资格提供程序的配置设置。 在 SQL Server 中创建成员资格架构教程中,我们检查了在其中Web.config指定成员资格提供程序配置设置。 表 2 包含配置设置的完整列表。

影响可能使用的重载的一个这样的成员资格提供程序配置设置 CreateUserrequiresQuestionAndAnswer 设置。 如果 requiresQuestionAndAnswer 设置为 true (默认值),则在创建新的用户帐户时,必须指定安全问题和答案。 如果用户需要重置或更改其密码,则稍后会使用此信息。 具体而言,此时它们会显示安全问题,必须输入正确的答案才能重置或更改其密码。 因此,如果设置为truerequiresQuestionAndAnswer,则调用前两CreateUser个重载中的任何一个会导致异常,因为缺少安全问题和答案。 由于应用程序当前配置为需要安全问题和答案,因此在以编程方式创建用户时,我们需要使用后两个重载之一。

为了说明如何使用此方法 CreateUser ,让我们创建一个用户界面,其中我们提示用户输入其名称、密码、电子邮件和预定义安全问题的答案。 打开文件夹中的页面MembershipCreatingUserAccounts.aspx并将以下 Web 控件添加到内容控件:

  • 名为 TextBox 的文本框 Username
  • 一个名为 PasswordTextBox,其 TextMode 属性设置为 Password
  • 名为 TextBox 的文本框 Email
  • 已清除其Text属性的标签SecurityQuestion
  • 名为 TextBox 的文本框 SecurityAnswer
  • 一个名为 CreateAccountButtonText 属性设置为“创建用户帐户”的按钮
  • 一个标签控件,其CreateAccountResultsText属性已清除

此时,屏幕应类似于图 6 中显示的屏幕截图。

将各种 Web 控件添加到CreatingUserAccounts.aspx页

图 6:向 添加各种 Web 控件 CreatingUserAccounts.aspx Page单击以查看全尺寸图像

SecurityQuestion Label 和 SecurityAnswer TextBox 旨在显示预定义的安全问题并收集用户的答案。 请注意,安全问题和答案都是按用户存储的,因此,允许每个用户定义自己的安全问题。 但是,对于此示例,我决定使用通用安全问题,即:你最喜欢的颜色是什么?

若要实现此预定义的安全问题,请将常量添加到名为 passwordQuestion 的页面代码隐藏类中,为其分配安全问题。 然后,在事件处理程序中 Page_Load ,将此常量分配给 SecurityQuestion Label 的属性 Text

Const passwordQuestion As String = "What is your favorite color"
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load 
 If Not Page.IsPostBack Then 
 SecurityQuestion.Text = passwordQuestion 
 End If 
End Sub

接下来,为 CreateAccountButton' s Click 事件创建事件处理程序并添加以下代码:

Protected Sub CreateAccountButton_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles CreateAccountButton.Click 
 Dim createStatus As MembershipCreateStatus 
 Dim newUser As MembershipUser = _ 
 Membership.CreateUser(Username.Text, Password.Text, _ 
 Email.Text, passwordQuestion, _ 
 SecurityAnswer.Text, True, _ 
 createStatus)
 Select Case createStatus 
 Case MembershipCreateStatus.Success 
 CreateAccountResults.Text = "The user account was successfully created!" 
 Case MembershipCreateStatus.DuplicateUserName 
 CreateAccountResults.Text = "There already exists a user with this username." 
 Case MembershipCreateStatus.DuplicateEmail 
 CreateAccountResults.Text = "There already exists a user with this email address." 
 Case MembershipCreateStatus.InvalidEmail
 CreateAccountResults.Text = "There email address you provided in invalid." 
 Case MembershipCreateStatus.InvalidAnswer 
 CreateAccountResults.Text = "There security answer was invalid." 
 Case MembershipCreateStatus.InvalidPassword 
 CreateAccountResults.Text = "The password you provided is invalid. It must be seven characters long and have at least one non-alphanumeric character." 
 Case Else 
 CreateAccountResults.Text = "There was an unknown error; the user account was NOT created."
 End Select 
End Sub

Click事件处理程序首先定义一个名为createStatus类型的MembershipCreateStatus变量。 MembershipCreateStatus 是一个枚举,指示操作的状态 CreateUser 。 例如,如果成功创建用户帐户,则生成的 MembershipCreateStatus 实例将设置为另一方面的值 Success; ,如果操作失败,因为已存在具有相同用户名的用户,它将设置为值 DuplicateUserName。 在使用的重载中 CreateUser ,需要将 MembershipCreateStatus 实例传递到方法中。 此参数设置为方法中的 CreateUser 相应值,我们可以在方法调用后检查其值,以确定是否已成功创建用户帐户。

调用 CreateUser后,传入 createStatus后, Select Case 语句用于输出适当的消息,具体取决于分配给 createStatus的值。 图 7 显示创建新用户成功时的输出。 图 8 和 9 显示未创建用户帐户时的输出。 在图 8 中,访问者输入了一个五字母密码,该密码不符合成员资格提供程序的配置设置中说明的密码强度要求。 在图 9 中,访问者尝试使用现有用户名(图 7 中创建的用户名)创建用户帐户。

新用户帐户已成功创建

图 7:已成功创建一个新用户帐户(单击以查看全尺寸图像

用户帐户未创建,因为提供的密码太弱

图 8:未创建用户帐户,因为提供的密码太弱(单击以查看全尺寸图像

用户帐户未创建,因为用户名已在使用中

图 9:未创建用户帐户,因为用户名已在使用中(单击以查看全尺寸图像

注意

你可能想知道在使用前两 CreateUser 个方法重载之一时如何确定成功或失败,这两个方法都没有类型 MembershipCreateStatus参数。 前两个重载在失败时引发异常,其中包括StatusCode类型MembershipCreateStatus属性MembershipCreateUserException

创建几个用户帐户后,请通过列出数据库中的内容 aspnet_Usersaspnet_MembershipSecurityTutorials.mdf 来验证是否已创建这些帐户。 如图 10 所示,我通过 CreatingUserAccounts.aspx 页面添加了两个用户:Tito 和 Bruce。

成员资格用户存储中有两个用户:Tito 和 Bruce

图 10:成员资格用户存储中有两个用户:Tito 和 Bruce(单击以查看全尺寸图像

虽然成员资格用户存储现在包括 Bruce 和 Tito 的帐户信息,但我们尚未实现允许 Bruce 或 Tito 登录到站点的功能。 目前,Login.aspx根据硬编码的用户名/密码对集验证用户的凭据 - 它不会根据成员资格框架验证提供的凭据。 现在,查看表中aspnet_Usersaspnet_Membership的新用户帐户必须足够。 在下一教程中, 对成员资格用户存储验证用户凭据,我们将更新登录页以针对成员资格存储进行验证。

注意

如果未在数据库中 SecurityTutorials.mdf 看到任何用户,则可能是因为 Web 应用程序使用的是默认成员身份提供程序, AspNetSqlMembershipProvider该提供程序使用 ASPNETDB.mdf 数据库作为其用户存储。 若要确定这是问题,请单击解决方案资源管理器中的“刷新”按钮。 如果已将已命名 ASPNETDB.mdf 的数据库添加到 App_Data 该文件夹中,则这是问题。 返回到 SQL Server 中创建成员资格架构教程的步骤 4,获取有关如何正确配置成员资格提供程序的说明。

在大多数创建用户帐户方案中,访问者会显示一些界面,用于输入其用户名、密码、电子邮件和其他重要信息,此时会创建新帐户。 在此步骤中,我们亲手查看了如何构建此类接口,然后了解如何使用 Membership.CreateUser 该方法根据用户的输入以编程方式添加新用户帐户。 但是,我们的代码刚刚创建了新的用户帐户。 它未执行任何后续操作,例如登录到刚刚创建的用户帐户下的站点,或向用户发送确认电子邮件。 这些附加步骤需要 Button 事件处理程序 Click 中的其他代码。

ASP.NET 附带 CreateUserWizard 控件,该控件旨在处理用户帐户创建过程,从呈现用于创建新用户帐户的用户界面、在成员身份框架中创建帐户和执行帐户创建后任务,例如发送确认电子邮件并将刚刚创建的用户记录到网站。 使用 CreateUserWizard 控件非常简单,只需将 CreateUserWizard 控件从工具箱拖动到页面上,然后设置几个属性。 在大多数情况下,无需编写一行代码。 我们将在步骤 6 中详细探讨此漂亮的控件。

如果新用户帐户仅通过典型的“创建帐户”网页创建,则不太可能需要编写使用该 CreateUser 方法的代码,因为 CreateUserWizard 控件可能会满足你的需求。 但是,在 CreateUser 需要高度自定义的“创建帐户”用户体验的情况下,或者需要通过替代界面以编程方式创建新用户帐户时,此方法非常有用。 例如,你可能有一个页面,允许用户上传包含来自其他应用程序的用户信息的 XML 文件。 该页可能会分析上传的 XML 文件的内容,并通过调用 CreateUser 该方法为 XML 中表示的每个用户创建新帐户。

步骤 6:使用 CreateUserWizard 控件创建新用户

ASP.NET 附带许多登录 Web 控件。 这些控制有助于许多常见的用户帐户和登录相关方案。 CreateUserWizard 控件是一个此类控件,旨在提供用于向成员资格框架添加新用户帐户的用户界面。

与其他许多与登录相关的 Web 控件一样,CreateUserWizard 无需编写一行代码即可使用。 它直观地根据成员资格提供程序的配置设置提供用户界面,并在用户输入必要信息并单击“创建用户”按钮后在内部调用 MembershipCreateUser 的方法。 CreateUserWizard 控件是极其可自定义的。 帐户创建过程的各个阶段都触发了一系列事件。 我们可以根据需要创建事件处理程序,将自定义逻辑注入帐户创建工作流。 此外,CreateUserWizard 的外观非常灵活。 有许多属性定义默认接口的外观;如有必要,可以将控件转换为模板,也可以添加其他用户注册步骤。

让我们从使用 CreateUserWizard 控件的默认接口和行为开始。 然后,我们将探讨如何通过控件的属性和事件自定义外观。

检查 CreateUserWizard 的默认接口和行为

返回到 CreatingUserAccounts.aspx 文件夹中的页面 Membership ,切换到“设计”或“拆分”模式,然后将 CreateUserWizard 控件添加到页面顶部。 CreateUserWizard 控件在工具箱的登录控件部分下提交。 添加控件后,将其 ID 属性设置为 RegisterUser. 如图 11 所示,CreateUserWizard 呈现一个界面,其中包含新用户的用户名、密码、电子邮件地址以及安全问题和答案的文本框。

CreateUserWizard 控件呈现泛型创建用户界面

图 11:CreateUserWizard 控件呈现通用创建用户界面(单击以查看全尺寸图像

让我们花点时间将 CreateUserWizard 控件生成的默认用户界面与我们在步骤 5 中创建的接口进行比较。 对于初学者,CreateUserWizard 控件允许访问者同时指定安全问题和答案,而手动创建的接口使用预定义的安全问题。 CreateUserWizard 控件的接口还包括验证控件,而我们尚未在接口的表单字段中实现验证。 CreateUserWizard 控件界面包括确认密码文本框(以及 CompareValidator 以确保输入的密码和比较密码文本框的文本相等)。

有趣的是,CreateUserWizard 控件在呈现其用户界面时会查阅成员资格提供程序的配置设置。 例如,仅当设置为 True 时 requiresQuestionAndAnswer ,才会显示安全问题和答案文本框。 同样,CreateUserWizard 会自动添加 RegularExpressionValidator 控件,以确保满足密码强度要求,并基于minRequiredPasswordLength配置设置设置和设置其ErrorMessage属性ValidationExpressionpasswordStrengthRegularExpressionminRequiredNonalphanumericCharacters

CreateUserWizard 控件(顾名思义)派生自 向导控件。 向导控件旨在提供用于完成多步骤任务的接口。 向导控件可能具有任意数目 WizardSteps,每个控件都是定义该步骤的 HTML 和 Web 控件的模板。 向导控件最初显示第一个 WizardStep控件,以及允许用户从一个步骤继续到下一步的导航控件,或返回到前面的步骤。

如图 11 所示的声明性标记,CreateUserWizard 控件的默认接口包括两 WizardStep 个:

  • CreateUserWizardStep ? 呈现用于收集用于创建新用户帐户的信息的界面。 这是图 11 中显示的步骤。
  • CompleteWizardStep ? 呈现一条消息,指示已成功创建帐户。

CreateUserWizard 的外观和行为可以通过将这些步骤之一转换为模板或添加自己的 WizardStep 模板来修改。 我们将了解如何WizardStep在“存储其他用户信息”教程中添加注册界面。

让我们看看 CreateUserWizard 控件在操作中。 CreatingUserAccounts.aspx通过浏览器访问页面。 首先,在 CreateUserWizard 的接口中输入一些无效值。 尝试输入不符合密码强度要求的密码,或将“用户名”文本框留空。 CreateUserWizard 将显示相应的错误消息。 图 12 显示尝试创建密码不足的用户时的输出。

CreateUserWizard 自动注入验证控件

图 12:CreateUserWizard 自动注入验证控件(单击以查看全尺寸图像

接下来,在 CreateUserWizard 中输入适当的值,然后单击“创建用户”按钮。 假设输入了必填字段并且密码强度已足够,CreateUserWizard 将通过成员资格框架创建新的用户帐户,然后显示 CompleteWizardStep“界面”(请参阅图 13)。 在后台,CreateUserWizard 调用该方法 Membership.CreateUser ,就像我们在步骤 5 中所做的一样。

新用户帐户已成功创建

图 13:已成功创建新的用户帐户(单击以查看全尺寸图像

注意

如图 13 所示, CompleteWizardStep's 接口包括“继续”按钮。 但是,此时单击它只是执行回发,使访问者在同一页上。 在“自定义 CreateUserWizard 的外观和行为通过其属性”部分,我们将了解如何让此按钮将访问者 Default.aspx 发送到(或其他页面)。

创建新用户帐户后,返回到 Visual Studio 并检查图 10 中所示的 aspnet_Usersaspnet_Membership 表,以验证该帐户是否已成功创建。

通过 CreateUserWizard 的属性自定义其行为和外观

CreateUserWizard 可以通过各种方式(通过属性、 WizardStep s 和事件处理程序)进行自定义。 在本部分中,我们将了解如何通过控件的属性自定义控件的外观;下一部分将介绍如何通过事件处理程序扩展控件的行为。

几乎所有在 CreateUserWizard 控件的默认用户界面中显示的文本都可以通过其大量属性进行自定义。 例如,可在文本框左侧显示的“用户名”、“密码”、“确认密码”、“电子邮件”、“安全问题”和安全答案标签分别由UserNameLabelTextPasswordLabelText文本框的“、”、“ConfirmPasswordLabelText”、“EmailLabelTextQuestionLabelTextAnswerLabelText“属性”自定义。 同样,还有一些属性用于指定“创建用户”和“继续”按钮的文本CreateUserWizardStepCompleteWizardStep,以及这些按钮是否呈现为 Buttons、LinkButtons 或 ImageButtons。

颜色、边框、字体和其他视觉元素可通过一系列样式属性进行配置。 CreateUserWizard 控件本身具有常见的 Web 控件样式属性(BackColorBorderStyleCssClassFont等等),并且有许多样式属性用于定义 CreateUserWizard 接口的特定部分的外观。 例如,该属性定义文本框中的CreateUserWizardStep样式,而TitleTextStyle该属性定义标题的样式(注册新帐户)。TextBoxStyle

除了与外观相关的属性外,还有一些影响 CreateUserWizard 控件行为的属性。 如果该 DisplayCancelButton 属性设置为 True,则显示“创建用户”按钮旁边的“取消”按钮(默认值为 False)。 如果显示“取消”按钮,请确保还设置 CancelDestinationPageUrl 属性,该属性指定用户单击“取消”后将发送到的页面。 如上一部分所述,界面中的 CompleteWizardStep“继续”按钮会导致回发,但使访问者在同一页上。 若要在单击“继续”按钮后将访问者发送到其他页面,只需在属性ContinueDestinationPageUrl指定 URL。

让我们更新 RegisterUser CreateUserWizard 控件以显示“取消”按钮,并在单击“取消”或“继续”按钮时将访问者 Default.aspx 发送到该控件。 为此,请将DisplayCancelButton属性设置为 True,并将CancelDestinationPageUrlContinueDestinationPageUrl属性设置为 ~/Default.aspx。 图 14 显示通过浏览器查看时更新的 CreateUserWizard。

CreateUserWizardStep 包含取消按钮

图 14CreateUserWizardStep 包括“取消”按钮(单击可查看全尺寸图像

当访问者输入用户名、密码、电子邮件地址以及安全问题和答案,然后单击“创建用户”时,将创建一个新用户帐户,并将访问者作为新创建的用户登录。 假设访问页面的人员正在为自己创建新帐户,这可能是所需的行为。 但是,你可能希望允许管理员添加新的用户帐户。 这样做时,将创建用户帐户,但管理员将保持以管理员身份登录(而不是新创建的帐户)。 可以通过布尔 LoginCreatedUser 属性修改此行为。

成员身份框架中的用户帐户包含已批准的标志;未批准的用户无法登录到站点。 默认情况下,新创建的帐户标记为已批准,允许用户立即登录站点。 但是,可以让新用户帐户标记为未经批准。 你可能希望管理员在登录之前手动批准新用户;或者,在允许用户登录之前,可能需要验证在注册时输入的电子邮件地址是否有效。 无论情况如何,都可以通过将 CreateUserWizard 控件 DisableCreatedUser 的属性 设置为 True(默认值为 False)来将新创建的用户帐户标记为未批准。

注释的其他行为相关属性包括 AutoGeneratePasswordMailDefinitionAutoGeneratePassword如果该属性设置为 True,则CreateUserWizardStep不会显示“密码”和“确认密码”文本框;而是使用MembershipGeneratePassword的方法自动生成新创建的用户的密码。 该方法 GeneratePassword 构造长度为指定长度的密码,并具有足够数量的非字母数字字符来满足配置的密码强度要求。

如果要向帐户创建过程中指定的电子邮件地址发送电子邮件,则 MailDefinition 此属性 非常有用。 该 MailDefinition 属性包括一系列子属性,用于定义有关构造的电子邮件的信息。 这些子属性包括诸如SubjectPriorityIsBodyHtmlFromCCBodyFileName。 该 BodyFileName 属性 指向包含电子邮件正文的文本或 HTML 文件。 正文支持两个预定义的占位符: <%UserName%><%Password%>。 这些占位符(如果存在于 BodyFileName 文件中)将替换为刚刚创建的用户名和密码。

注意

控件CreateUserWizardMailDefinition的属性只是指定创建新帐户时发送的电子邮件的详细信息。 它不包括有关电子邮件实际发送方式的任何详细信息(即使用 SMTP 服务器还是邮件删除目录、任何身份验证信息等)。 需要在本节Web.config<system.net>定义这些低级别的详细信息。 有关这些配置设置以及从 ASP.NET 2.0 发送电子邮件的详细信息,请参阅 SystemNetMail.com 的常见问题解答和我的文章, ASP.NET 2.0 发送电子邮件。

使用事件处理程序扩展 CreateUserWizard 的行为

CreateUserWizard 控件在其工作流中引发多个事件。 例如,访问者输入用户名、密码和其他相关信息并单击“创建用户”按钮后,CreateUserWizard 控件将引发其 CreatingUser 事件。 如果在创建过程中出现问题, CreateUserError 则会触发该事件 ;但是,如果成功创建用户,则会 CreatedUser 引发该事件 。 还有其他 CreateUserWizard 控件事件会引发,但这些事件是三个最德国的控件事件。

在某些情况下,我们可能需要利用 CreateUserWizard 工作流,为此,我们可以为相应的事件创建事件处理程序。 为了说明这一点,我们来增强 RegisterUser CreateUserWizard 控件,以在用户名和密码上包括一些自定义验证。 具体而言,我们来增强 CreateUserWizard,以便用户名不能包含前导空格或尾随空格,并且用户名不能出现在密码中的任何位置。 简言之,我们希望阻止某人创建用户名(如“Scott”)或具有斯科特和 Scott.1234 等用户名/密码组合。

为此,我们将为 CreatingUser 事件创建事件处理程序来执行额外的验证检查。 如果提供的数据无效,则需要取消创建过程。 我们还需要向页面添加标签 Web 控件,以显示说明用户名或密码无效的消息。 首先在 CreateUserWizard 控件下面添加一个 Label 控件,将其 ID 属性 InvalidUserNameOrPasswordMessage 设置为其属性并将其 ForeColor 属性设置为 Red。 清除其Text属性并将其和Visible属性设置为 EnableViewState False。

<asp:Label runat="server"" id="InvalidUserNameOrPasswordMessage"
 Visible="false" ForeColor="Red" EnableViewState="false"> 
</asp:Label>

接下来,为 CreateUserWizard 控件 CreatingUser 的事件创建事件处理程序。 若要创建事件处理程序,请在设计器中选择控件,然后转到属性窗口。 在此处,单击闪电图标,然后双击相应的事件以创建事件处理程序。

将以下代码添加到 CreatingUser 事件处理程序中:

Protected Sub RegisterUser_CreatingUser(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.LoginCancelEventArgs) Handles RegisterUser.CreatingUser
 Dim trimmedUserName As String = RegisterUser.UserName.Trim() 
 If RegisterUser.UserName.Length <> trimmedUserName.Length Then 
 ' Show the error message 
 InvalidUserNameOrPasswordMessage.Text = "The username cannot contain leading or trailing spaces." 
 InvalidUserNameOrPasswordMessage.Visible = True 
 ' Cancel the create user workflow 
 e.Cancel = True 
 Else 
 ' Username is valid, make sure that the password does not contain the username 
 If RegisterUser.Password.IndexOf(RegisterUser.UserName, StringComparison.OrdinalIgnoreCase) >= 0 Then 
 ' Show the error message 
 InvalidUserNameOrPasswordMessage.Text = "The username may not appear anywhere in the password." 
 InvalidUserNameOrPasswordMessage.Visible = True 
 ' Cancel the create user workflow 
 e.Cancel = True 
 End If 
 End If 
End Sub

请注意,在 CreateUserWizard 控件中输入的用户名和密码分别可通过其 UserName 属性和 Password 属性获得。 我们在上述事件处理程序中使用这些属性来确定提供的用户名是否包含前导空格或尾随空格,以及是否在密码中找到用户名。 如果满足上述任一条件,则会在 InvalidUserNameOrPasswordMessage Label 中显示错误消息,并且事件处理程序 e.Cancel 的属性设置为 True。 如果 e.Cancel 设置为 TrueCreateUserWizard,则会中断其工作流,从而有效取消用户帐户创建过程。

图 15 显示了用户输入带前导空格的用户名时的屏幕截图 CreatingUserAccounts.aspx

不允许使用前导或尾随空格的用户名

图 15:不允许使用前导或尾随空格的用户名(单击可查看全尺寸图像

注意

我们将在“存储其他用户信息”教程中看到使用 CreateUserWizard 控件CreatedUser事件的示例。

总结

MembershipCreateUser的方法在成员资格框架中创建一个新的用户帐户。 它通过委托对配置的成员资格提供程序的调用来执行此操作。 在这种情况下SqlMembershipProvider,该方法CreateUser向表和aspnet_Membership数据库表添加记录aspnet_Users

虽然可以通过编程方式(如步骤 5 中所示)创建新用户帐户,但使用 CreateUserWizard 控件的速度更快、更简单的方法。 此控件呈现多步骤用户界面,用于收集用户信息并在成员资格框架中创建新用户。 在后台,此控件使用与步骤 5 中检查的方法相同 Membership.CreateUser ,但该控件会创建用户界面、验证控件并响应用户帐户创建错误,而无需编写代码。

此时,我们有了创建新用户帐户的功能。 但是,登录页仍在针对我们在第二个教程中指定的那些硬编码凭据进行验证。 在下一教程中,我们将更新Login.aspx以根据成员资格框架验证用户提供的凭据。

快乐编程!

深入阅读

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

关于作者

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

特别感谢

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