向用户分配角色 (C#)

作者 :Scott Mitchell

注意

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

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

下载代码下载 PDF

在本教程中,我们将生成两个 ASP.NET 页面,以帮助管理哪些用户属于哪些角色。 第一页将包括用于查看哪些用户属于给定角色、特定用户所属的角色以及从特定角色中分配或删除特定用户的功能。 第二页将扩充 CreateUserWizard 控件,使其包含一个步骤来指定新创建的用户所属的角色。 这在管理员能够创建新用户帐户的情况下非常有用。

简介

一教程 检查了 Roles 框架和 SqlRoleProvider;我们了解了如何使用 Roles 类创建、检索和删除角色。 除了创建和删除角色外,还需要能够分配或删除角色中的用户。 遗憾的是,ASP.NET 不随任何 Web 控件一起提供,用于管理哪些用户属于哪些角色。 相反,我们必须创建自己的 ASP.NET 页来管理这些关联。 好消息是,在角色中添加和删除用户非常简单。 类 Roles 包含许多方法,用于将一个或多个用户添加到一个或多个角色。

在本教程中,我们将生成两个 ASP.NET 页面,以帮助管理哪些用户属于哪些角色。 第一页将包括用于查看哪些用户属于给定角色、特定用户所属的角色以及从特定角色中分配或删除特定用户的功能。 第二页将扩充 CreateUserWizard 控件,使其包含一个步骤来指定新创建的用户所属的角色。 这在管理员能够创建新用户帐户的情况下非常有用。

让我们开始吧!

列出哪些用户属于哪些角色

本教程的第一个业务顺序是创建可从中向用户分配角色的网页。 在关注如何将用户分配到角色之前,让我们先集中讨论如何确定哪些用户属于哪些角色。 可通过两种方式显示此信息:“按角色”或“按用户”。我们可以允许访问者选择一个角色,然后向他们显示属于该角色的所有用户 (“按角色”显示) ,也可以提示访问者选择用户,然后向他们显示分配给该用户的角色 (“按用户”显示) 。

在访问者想要了解属于特定角色的用户集的情况下,“按角色”视图非常有用;当访问者需要知道特定用户的角色 () 时,“按用户”视图是理想选择。 让我们的页面同时包含“按角色”和“按用户”界面。

我们将从创建“按用户”界面开始。 此接口将包含一个下拉列表和一个复选框列表。 下拉列表中将填充系统中的一组用户;复选框将枚举角色。 从下拉列表中选择一个用户将检查该用户所属的角色。 然后,访问该页面的人员可以检查或取消选中复选框,以在相应角色中添加或删除所选用户。

注意

对于可能有数百个用户帐户的网站,使用下拉列表列出用户帐户不是理想的选择。 下拉列表旨在允许用户从相对较短的选项列表中选择一项。 随着列表项数的增长,它很快就会变得难以处理。 如果要构建可能具有大量用户帐户的网站,则可能需要考虑使用备用用户界面,例如可分页的 GridView 或可筛选界面,该界面列出提示访问者选择一个字母,然后仅显示用户名以所选字母开头的用户。

步骤 1:生成“按用户”用户界面

打开 UsersAndRoles.aspx 页面。 在页面顶部,添加一个名为 ActionStatus 的标签 Web 控件并清除其 Text 属性。 我们将使用此标签提供有关所执行的操作的反馈,并显示诸如“用户 Tito 已添加到管理员角色”或“用户 Jisun 已从监督员角色中删除”等消息。为了使这些消息脱颖而出,请将 Label 的 CssClass 属性设置为“Important”。

<p align="center"> 

     <asp:Label ID="ActionStatus" runat="server" CssClass="Important"></asp:Label> 
</p>

接下来,将以下 CSS 类定义添加到 Styles.css 样式表中:

.Important 
{ 
     font-size: large; 
     color: Red; 
}

此 CSS 定义指示浏览器使用红色大字体显示标签。 图 1 通过 Visual Studio Designer显示了此效果。

Label 的 CssClass 属性生成大红色字体

图 1:标签的属性 CssClass 导致大红色字体 (单击以查看全尺寸图像)

接下来,将 DropDownList 添加到页面,将其 ID 属性设置为 UserList,并将其 属性设置为 AutoPostBack True。 我们将使用此 DropDownList 列出系统中的所有用户。 此 DropDownList 将绑定到 MembershipUser 对象的集合。 由于我们希望 DropDownList (显示 MembershipUser 对象的 UserName 属性并将其用作) 列表项的值,因此请将 DropDownList 的 DataTextFieldDataValueField 属性设置为“UserName”。

在 DropDownList 下面,添加一个名为 的 UsersRoleListRepeater。 此中继器将以一系列复选框的形式列出系统中的所有角色。 使用以下声明性标记定义中继器 ItemTemplate

<asp:Repeater ID="UsersRoleList" runat="server"> 
     <ItemTemplate> 
          <asp:CheckBox runat="server" ID="RoleCheckBox" AutoPostBack="true" 

               Text='<%# Container.DataItem %>' /> 
          <br /> 
     </ItemTemplate> 
</asp:Repeater>

标记 ItemTemplate 包含名为 的 RoleCheckBox单个 CheckBox Web 控件。 CheckBox 的 AutoPostBack 属性设置为 True,并且 属性 Text 绑定到 Container.DataItem。 数据绑定语法的原因只是 Container.DataItem 因为 Roles 框架将角色名称列表作为字符串数组返回,而我们将绑定到 Repeater 的正是此字符串数组。 此语法为何用于显示绑定到数据 Web 控件的数组内容的完整说明超出了本教程的范围。 有关此问题的详细信息,请参阅 将标量数组绑定到数据 Web 控件

此时,“by user”接口的声明性标记应如下所示:

<h3>Manage Roles By User</h3> 

<p> 
     <b>Select a User:</b> 
     <asp:DropDownList ID="UserList" runat="server" AutoPostBack="True" 
          DataTextField="UserName" DataValueField="UserName"> 

     </asp:DropDownList> 
</p> 
<p> 
     <asp:Repeater ID="UsersRoleList" runat="server"> 
          <ItemTemplate> 
               <asp:CheckBox runat="server" ID="RoleCheckBox" AutoPostBack="true" 

                    Text='<%# Container.DataItem %>' /> 
               <br /> 
          </ItemTemplate> 
     </asp:Repeater> 
</p>

现在,我们已准备好编写代码,将用户帐户集绑定到 DropDownList,并将角色集绑定到 Repeater。 在页面的代码隐藏类中,使用以下代码添加名为 BindUsersToUserList 的方法和另一个名为 BindRolesList的方法:

private void BindUsersToUserList() 
{ 
     // Get all of the user accounts 
     MembershipUserCollection users = Membership.GetAllUsers(); 
     UserList.DataSource = users; 
     UserList.DataBind(); 
}
 
private void BindRolesToList() 
{ 
     // Get all of the roles 
     string[] roles = Roles.GetAllRoles(); 
     UsersRoleList.DataSource = roles; 
     UsersRoleList.DataBind(); 
}

方法 BindUsersToUserList 通过 Membership.GetAllUsers 方法检索系统中的所有用户帐户。 这将返回 一个 MembershipUserCollection 对象,该对象是 实例的MembershipUser集合。 然后,此集合将绑定到 UserList DropDownList。 MembershipUser构成集合的实例包含各种属性,如 UserNameEmailCreationDateIsOnline。 若要指示 DropDownList 显示属性的值 UserName ,请确保 UserList DropDownList 的 DataTextFieldDataValueField 属性已设置为“UserName”。

注意

方法 Membership.GetAllUsers 有两个重载:一个重载不接受任何输入参数并返回所有用户,一个重载接受页面索引和页面大小的整数值,仅返回指定的用户子集。 当在可分页用户界面元素中显示大量用户帐户时,可以使用第二个重载更有效地分页浏览用户,因为它只返回用户帐户的精确子集,而不是所有用户帐户。

方法 BindRolesToList 首先调用 Roles 类的 GetAllRoles 方法,该方法返回一个包含系统中角色的字符串数组。 然后,此字符串数组将绑定到 Repeater。

最后,我们需要在首次加载页面时调用这两种方法。 将以下代码添加到 Page_Load 事件处理程序中:

protected void Page_Load(object sender, EventArgs e) 
{ 
     if (!Page.IsPostBack) 
     { 
          // Bind the users and roles 
          BindUsersToUserList(); 
          BindRolesToList(); 
     } 
}

完成此代码后,请花点时间通过浏览器访问页面;屏幕应类似于图 2。 所有用户帐户都填充在下拉列表中,并在该下拉列表下方,每个角色显示为一个复选框。 由于我们将 DropDownList 和 CheckBoxes 的属性设置为 AutoPostBack True,因此更改所选用户或选中或取消选中角色会导致回发。 但是,不会执行任何操作,因为我们尚未编写代码来处理这些操作。 我们将在接下来的两个部分中处理这些任务。

页面显示用户和角色

图 2:页面显示用户和角色 (单击以查看全尺寸图像)

检查所选用户所属的角色

首次加载页面时,或每当访问者从下拉列表中选择新用户时,我们需要更新 UsersRoleList的复选框,以便仅当所选用户属于该角色时才选中给定角色复选框。 为此,请使用以下代码创建名为 CheckRolesForSelectedUser 的方法:

private void CheckRolesForSelectedUser() 
{ 
     // Determine what roles the selected user belongs to 
     string selectedUserName = UserList.SelectedValue; 
     string[] selectedUsersRoles = Roles.GetRolesForUser(selectedUserName); 

     // Loop through the Repeater's Items and check or uncheck the checkbox as needed 

     foreach (RepeaterItem ri in UsersRoleList.Items) 
     { 
          // Programmatically reference the CheckBox 
          CheckBox RoleCheckBox = ri.FindControl("RoleCheckBox") as CheckBox; 
          // See if RoleCheckBox.Text is in selectedUsersRoles 
          if (selectedUsersRoles.Contains<string>(RoleCheckBox.Text)) 
               RoleCheckBox.Checked = true; 
          else 
               RoleCheckBox.Checked = false; 
     } 
}

上述代码首先确定所选用户是谁。 然后,它使用 Roles 类的 GetRolesForUser(userName) 方法 以字符串数组的形式返回指定用户的角色集。 接下来,枚举 Repeater 的项,并以编程方式引用每个项的 RoleCheckBox CheckBox。 仅当复选框对应的角色包含在字符串数组中 selectedUsersRoles 时,才会选中该复选框。

注意

selectedUserRoles.Contains<string>(...)如果使用 ASP.NET 2.0 版,则不会编译语法。 方法 Contains<string>LINQ 库的一部分,该库是 ASP.NET 3.5 中的新增功能。 如果仍在使用 ASP.NET 版本 2.0,请改用 Array.IndexOf<string> 方法

CheckRolesForSelectedUser需要在两种情况下调用 方法:首次加载页面时和 DropDownList UserList 的选定索引发生更改时。 因此,在调用 和 BindRolesToList) 后,从Page_Load事件处理程序 (调用BindUsersToUserList此方法。 此外,请为 DropDownList 的事件 SelectedIndexChanged 创建事件处理程序,并从那里调用此方法。

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

          // Bind the users and roles 
          BindUsersToUserList(); 
          BindRolesToList(); 
          // Check the selected user's roles 
          CheckRolesForSelectedUser(); 
     } 
} 

... 

protected void UserList_SelectedIndexChanged(object sender, EventArgs e) 
{ 
     CheckRolesForSelectedUser(); 
}

完成此代码后,可以通过浏览器测试页面。 但是,由于 UsersAndRoles.aspx 页面当前无法将用户分配到角色,没有用户具有角色。 我们将创建一个界面,以便稍后将用户分配到角色,以便你可以采用我的话来验证此代码是否正常工作,也可以通过将记录插入 aspnet_UsersInRoles 表中来手动将用户添加到角色,以便立即测试此功能。

分配和删除角色中的用户

当访问者在 Repeater 中 UsersRoleList 检查或取消选中 CheckBox 时,我们需要在相应的角色中添加或删除所选用户。 CheckBox 的 AutoPostBack 属性当前设置为 True,这会导致在中继器中的 CheckBox 处于选中或取消选中状态时回发。 简而言之,我们需要为 CheckBox 的事件 CheckChanged 创建事件处理程序。 由于 CheckBox 位于 Repeater 控件中,因此需要手动添加事件处理程序管道。 首先,将事件处理程序作为 protected 方法添加到代码隐藏类,如下所示:

protected void RoleCheckBox_CheckChanged(object sender, EventArgs e) 
{ 

}

稍后我们将返回编写此事件处理程序的代码。 但首先让我们完成处理管道的事件。 在 Repeater 的 ItemTemplate中的 CheckBox 中添加 OnCheckedChanged="RoleCheckBox_CheckChanged"。 此语法将 RoleCheckBox_CheckChanged 事件处理程序 RoleCheckBox连接到 的 CheckedChanged 事件。

<asp:CheckBox runat="server" ID="RoleCheckBox" 
     AutoPostBack="true" 
     Text='<%# Container.DataItem %>' 
     OnCheckedChanged="RoleCheckBox_CheckChanged" />

我们最后的任务是完成 RoleCheckBox_CheckChanged 事件处理程序。 首先,我们需要引用引发事件的 CheckBox 控件,因为此 CheckBox 实例告诉我们已通过其 TextChecked 属性选中或取消选中的角色。 使用此信息以及所选用户的 UserName,我们通过 类的 AddUserToRoleRemoveUserFromRole 方法在角色Roles中添加或删除用户。

protected void RoleCheckBox_CheckChanged(object sender, EventArgs e) 
{ 
     // Reference the CheckBox that raised this event 
     CheckBox RoleCheckBox = sender as CheckBox; 

     // Get the currently selected user and role 
     string selectedUserName = UserList.SelectedValue; 

     string roleName = RoleCheckBox.Text; 

     // Determine if we need to add or remove the user from this role 
     if (RoleCheckBox.Checked) 
     { 
          // Add the user to the role 
          Roles.AddUserToRole(selectedUserName, roleName); 
          // Display a status message 
          ActionStatus.Text = string.Format("User {0} was added to role {1}.", selectedUserName, roleName); 
     } 
     else 
     { 
          // Remove the user from the role 
          Roles.RemoveUserFromRole(selectedUserName, roleName); 
          // Display a status message 
          ActionStatus.Text = string.Format("User {0} was removed from role {1}.", selectedUserName, roleName); 

     } 
}

上述代码首先以编程方式引用引发 事件的 CheckBox,该事件可通过 sender 输入参数获取。 如果选中 CheckBox,则所选用户将添加到指定角色,否则将从该角色中删除这些用户。 在任一情况下, ActionStatus 标签都显示一条消息,其中汇总了刚刚执行的操作。

请花点时间通过浏览器测试此页面。 选择“用户 Tito”,然后将 Tito 添加到“管理员”和“主管”角色。

Tito 已添加到管理员和监督员角色

图 3:Tito 已添加到管理员和主管角色 (单击以查看全尺寸图像)

接下来,从下拉列表中选择“用户 Bruce”。 有回发,中继器的 CheckBox 通过 CheckRolesForSelectedUser更新。 由于 Bruce 尚不属于任何角色,因此取消选中这两个复选框。 接下来,将 Bruce 添加到“主管”角色。

Bruce 已被添加到主管角色

图 4:Bruce 已添加到主管角色 (单击以查看全尺寸图像)

若要进一步验证方法的功能 CheckRolesForSelectedUser ,请选择 Tito 或 Bruce 以外的用户。 请注意复选框如何自动取消选中,表示它们不属于任何角色。 返回到 Tito。 应选中“管理员”和“主管”复选框。

步骤 2:生成“按角色”用户界面

此时,我们已经完成了“按用户”界面,并已准备好开始处理“按角色”接口。 “按角色”界面提示用户从下拉列表中选择一个角色,然后在 GridView 中显示属于该角色的用户集。

将另一个 DropDownList 控件添加到页面 UsersAndRoles.aspx 。 将此名称放在 Repeater 控件下,将其 RoleList命名为 ,并将其 AutoPostBack 属性设置为 True。 在它下面,添加一个 GridView 并将其命名为 RolesUserList。 此 GridView 将列出属于所选角色的用户。 将 GridView 的 AutoGenerateColumns 属性设置为 False,将 TemplateField 添加到网格的 Columns 集合,并将其属性设置为 HeaderText “Users”。 定义 TemplateField 的 ItemTemplate ,使其在名为 UserNameLabel的 Label 的 属性中Text显示数据绑定表达式Container.DataItem的值。

添加并配置 GridView 后,“按角色”接口的声明性标记应如下所示:

<h3>Manage Users By Role</h3> 
<p> 
     <b>Select a Role:</b> 

     <asp:DropDownList ID="RoleList" runat="server" AutoPostBack="true"></asp:DropDownList> 
</p> 
<p>      <asp:GridView ID="RolesUserList" runat="server" AutoGenerateColumns="false" 

          EmptyDataText="No users belong to this role."> 
          <Columns> 
               <asp:TemplateField HeaderText="Users"> 
                    <ItemTemplate> 
                         <asp:Label runat="server" id="UserNameLabel" 
                              Text='<%# Container.DataItem %>'></asp:Label> 

                    </ItemTemplate> 
               </asp:TemplateField> 
          </Columns> 
     </asp:GridView> </p>

我们需要使用 RoleList 系统中的一组角色填充 DropDownList。 为此,请更新 BindRolesToList 方法,以便将 该方法RolesList返回的Roles.GetAllRoles字符串数组绑定到 DropDownList (以及 UsersRoleList Repeater) 。

private void BindRolesToList() 
{ 
     // Get all of the roles 

     string[] roles = Roles.GetAllRoles(); 
     UsersRoleList.DataSource = roles; 
     UsersRoleList.DataBind(); 

     RoleList.DataSource = roles; 
     RoleList.DataBind(); 
}

添加了 方法中的 BindRolesToList 最后两行,以将角色集绑定到 RoleList DropDownList 控件。 图 5 显示了通过浏览器查看时的最终结果 - 填充了系统角色的下拉列表。

角色显示在 RoleList DropDownList 中

图 5:角色显示在 RoleList DropDownList 中 (单击以查看全尺寸图像)

显示属于所选角色的用户

首次加载页面或从 RoleList DropDownList 中选择新角色时,我们需要在 GridView 中显示属于该角色的用户列表。 使用以下代码创建名为 DisplayUsersBelongingToRole 的方法:

private void DisplayUsersBelongingToRole() 
{ 
     // Get the selected role 
     string selectedRoleName = RoleList.SelectedValue; 

     // Get the list of usernames that belong to the role 
     string[] usersBelongingToRole = Roles.GetUsersInRole(selectedRoleName); 

     // Bind the list of users to the GridView 
     RolesUserList.DataSource = usersBelongingToRole; 
     RolesUserList.DataBind(); 
}

此方法首先从 DropDownList 获取 RoleList 所选角色。 然后,Roles.GetUsersInRole(roleName)它使用 方法检索属于该角色的用户的 UserNames 的字符串数组。 然后,此数组将绑定到 RolesUserList GridView。

需要在两种情况下调用此方法:最初加载页面时和 DropDownList 中 RoleList 所选角色发生更改时。 因此,请 Page_Load 更新事件处理程序,以便在调用 CheckRolesForSelectedUser后调用此方法。 接下来,为 RoleListSelectedIndexChanged 事件创建事件处理程序,并从中也调用此方法。

protected void Page_Load(object sender, EventArgs e) 
{ 
     if (!Page.IsPostBack) 
     { 
          // Bind the users and roles 
          BindUsersToUserList(); 
          BindRolesToList(); 

          // Check the selected user's roles 
          CheckRolesForSelectedUser(); 

          // Display those users belonging to the currently selected role 
          DisplayUsersBelongingToRole(); 
     } 
} 

... 

protected void RoleList_SelectedIndexChanged(object sender, EventArgs e) 
{ 
     DisplayUsersBelongingToRole(); 
}

完成此代码后, RolesUserList GridView 应显示属于所选角色的用户。 如图 6 所示,主管角色由两名成员组成:Bruce 和 Tito。

GridView 列出属于所选角色的用户

图 6:GridView 列出属于所选角色的用户 (单击以查看全尺寸图像)

从所选角色中删除用户

让我们扩充 RolesUserList GridView,使其包含一列“删除”按钮。 单击特定用户的“删除”按钮会将他们从该角色中删除。

首先,将“删除”按钮字段添加到 GridView。 使此字段显示为最左边的字段,并将其 DeleteText 属性从“Delete” (默认) 更改为“Remove”。

显示如何在“字段”窗口中添加“删除”按钮的屏幕截图。

图 7:将“删除”按钮添加到 GridView (单击以查看全尺寸图像)

单击“删除”按钮时,会引发回发并引发 GridView 的事件 RowDeleting 。 我们需要为此事件创建事件处理程序,并编写从所选角色中删除用户的代码。 创建事件处理程序,然后添加以下代码:

protected void RolesUserList_RowDeleting(object sender, GridViewDeleteEventArgs e) 
{ 
     // Get the selected role 
     string selectedRoleName = RoleList.SelectedValue; 

     // Reference the UserNameLabel 
     Label UserNameLabel = RolesUserList.Rows[e.RowIndex].FindControl("UserNameLabel") as Label; 

     // Remove the user from the role 
     Roles.RemoveUserFromRole(UserNameLabel.Text, selectedRoleName); 

     // Refresh the GridView 
     DisplayUsersBelongingToRole(); 

     // Display a status message 
     ActionStatus.Text = string.Format("User {0} was removed from role {1}.", UserNameLabel.Text, selectedRoleName); 
}

代码首先确定所选角色名称。 然后,它以编程方式引用 UserNameLabel 单击了“删除”按钮的行中的控件,以确定要删除的用户的 UserName。 然后,通过调用 Roles.RemoveUserFromRole 方法将用户从角色中删除。 RolesUserList然后,将刷新 GridView,并通过 ActionStatus Label 控件显示一条消息。

注意

在将用户从角色中删除之前,“删除”按钮不需要用户进行任何形式的确认。 我邀请你添加某种级别的用户确认。 确认操作的最简单方法之一是通过客户端确认对话框。 有关此方法的详细信息,请参阅 在删除时添加Client-Side确认

图 8 显示了用户 Tito 已从“主管”组中删除后的页面。

唉, 蒂托不再是主管

图 8:唉, Tito 不再是主管 (单击以查看全尺寸图像)

将新用户添加到所选角色

除了从所选角色中删除用户外,此页面的访问者还应能够将用户添加到所选角色。 将用户添加到所选角色的最佳界面取决于预期拥有的用户帐户数。 如果你的网站只包含几十个或更少的用户帐户,则可以在此处使用 DropDownList。 如果可能有数千个用户帐户,则你需要包含一个用户界面,该用户界面允许访问者翻页浏览帐户、搜索特定帐户或以某种其他方式筛选用户帐户。

对于此页面,我们将使用一个非常简单的界面,无论系统中的用户帐户数如何,它都能正常工作。 也就是说,我们将使用 TextBox,提示访问者键入要添加到所选角色的用户的用户名。 如果不存在具有该名称的用户,或者用户已是该角色的成员,我们将在 Label 中 ActionStatus 显示一条消息。 但是,如果用户存在并且不是该角色的成员,我们将将其添加到角色并刷新网格。

在 GridView 下添加 TextBox 和 Button。 将 TextBox 的 ID 设置为 UserNameToAddToRole ,并将 Button 的 IDText 属性分别设置为 AddUserToRoleButton 和“将用户添加到角色”。

<p> 
     <b>UserName:</b> 
     <asp:TextBox ID="UserNameToAddToRole" runat="server"></asp:TextBox> 
     <br /> 
     <asp:Button ID="AddUserToRoleButton" runat="server" Text="Add User to Role" /> 

</p>

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

protected void AddUserToRoleButton_Click(object sender, EventArgs e) 
{ 
     // Get the selected role and username 

     string selectedRoleName = RoleList.SelectedValue; 
     string userNameToAddToRole = UserNameToAddToRole.Text; 

     // Make sure that a value was entered 
     if (userNameToAddToRole.Trim().Length == 0) 
     { 
          ActionStatus.Text = "You must enter a username in the textbox."; 
          return; 
     } 

     // Make sure that the user exists in the system 
     MembershipUser userInfo = Membership.GetUser(userNameToAddToRole); 
     if (userInfo == null) 
     { 
          ActionStatus.Text = string.Format("The user {0} does not exist in the system.", userNameToAddToRole); 

          return; 
     } 

     // Make sure that the user doesn't already belong to this role 
     if (Roles.IsUserInRole(userNameToAddToRole, selectedRoleName)) 
     { 
          ActionStatus.Text = string.Format("User {0} already is a member of role {1}.", userNameToAddToRole, selectedRoleName); 
          return; 
     } 

     // If we reach here, we need to add the user to the role 
     Roles.AddUserToRole(userNameToAddToRole, selectedRoleName); 

     // Clear out the TextBox 
     UserNameToAddToRole.Text = string.Empty; 

     // Refresh the GridView 
     DisplayUsersBelongingToRole(); 

     // Display a status message 

     ActionStatus.Text = string.Format("User {0} was added to role {1}.", userNameToAddToRole, selectedRoleName); }

事件处理程序中的 Click 大多数代码执行各种验证检查。 它确保访问者在 TextBox 中 UserNameToAddToRole 提供了用户名,用户存在于系统中,并且他们不属于所选角色。 如果其中任何检查失败,则会在 中 ActionStatus 显示相应的消息,并退出事件处理程序。 如果所有检查都通过,则用户将通过 Roles.AddUserToRole 方法添加到角色。 之后,将清除 TextBox 的 Text 属性,刷新 GridView,标签 ActionStatus 将显示一条消息,指示指定用户已成功添加到所选角色。

注意

为了确保指定的用户不属于所选角色,我们使用 Roles.IsUserInRole(userName, roleName) 方法,该方法返回一个布尔值,指示 userName 是否是 roleName 的成员。 在下一教程,我们将再次使用此方法,了解基于角色的授权。

通过浏览器访问页面,并从 RoleList DropDownList 中选择“主管”角色。 尝试输入无效的用户名 - 应会看到一条消息,说明系统中不存在该用户。

无法将不存在的用户添加到角色

图 9:无法将不存在的用户添加到角色 (单击以查看全尺寸图像)

现在,请尝试添加有效用户。 继续将 Tito 重新添加到“主管”角色。

蒂托再次成为主管!

图 10:蒂托再次成为主管! (单击以查看全尺寸图像)

步骤 3:交叉更新“按用户”和“按角色”接口

UsersAndRoles.aspx 页面提供了两个不同的界面,用于管理用户和角色。 目前,这两个接口彼此独立,因此在一个接口中所做的更改可能不会立即反映在另一个接口中。 例如,假设页面访问者从 RoleList DropDownList 中选择“主管”角色,该角色将 Bruce 和 Tito 列为其成员。 接下来,访问者从 UserList DropDownList 中选择 Tito,这将检查中继器中的 UsersRoleList “管理员”和“监督者”复选框。 如果访问者随后从中继器中取消选中“监督器”角色,则 Tito 将从“主管”角色中删除,但此修改不会反映在“按角色”接口中。 GridView 仍会将 Tito 显示为“主管”角色的成员。

若要解决此问题,每当在中继器中选中或取消选中 UsersRoleList 某个角色时,都需要刷新 GridView。 同样,每当从“按角色”界面删除或将用户添加到角色时,我们需要刷新中继器。

通过调用 CheckRolesForSelectedUser 方法刷新“by user”界面中的中继器。 可以在 GridView 的RowDeleting事件处理程序和 AddUserToRoleButton Button Click 的事件处理程序中RolesUserList修改“按角色”接口。 因此,我们需要从其中每个方法调用 CheckRolesForSelectedUser 方法。

protected void RolesUserList_RowDeleting(object sender, GridViewDeleteEventArgs e) 
{ 
     ... Code removed for brevity ... 

     // Refresh the "by user" interface 
     CheckRolesForSelectedUser(); 
} 

protected void AddUserToRoleButton_Click(object sender, EventArgs e) 
{ 
     ... Code removed for brevity ... 


     // Refresh the "by user" interface 
     CheckRolesForSelectedUser(); 
}

同样,通过调用 DisplayUsersBelongingToRole 方法刷新“by role”接口中的 GridView,并通过事件处理程序修改 RoleCheckBox_CheckChanged “by user”接口。 因此,我们需要从此事件处理程序调用 DisplayUsersBelongingToRole 方法。

protected void RoleCheckBox_CheckChanged(object sender, EventArgs e) 
{ 
     ... Code removed for brevity... 

     // Refresh the "by role" interface 
     DisplayUsersBelongingToRole(); 
}

通过这些次要代码更改,“按用户”和“按角色”接口现在可以正确交叉更新。 若要验证这一点,请通过浏览器访问页面,然后分别从 UserList 和 DropDownLists 中选择 Tito 和 RoleList Supervisors。 请注意,从“用户”界面的中继器中取消选中 Tito 的监督员角色时,Tito 会自动从“按角色”接口的 GridView 中删除。 从“按角色”界面将 Tito 添加回监督员角色后,会自动重新检查“按用户”界面中的“监督者”复选框。

步骤 4:自定义 CreateUserWizard 以包括“指定角色”步骤

创建用户帐户 教程中,我们了解了如何使用 CreateUserWizard Web 控件提供用于创建新用户帐户的界面。 CreateUserWizard 控件可通过以下两种方式之一使用:

  • 作为访问者在网站上创建其自己的用户帐户的一种手段,以及
  • 作为管理员创建新帐户的一种手段

在第一个用例中,访问者来到网站并填写 CreateUserWizard,输入其信息以在网站上注册。 第二种情况是,管理员为另一个人创建新帐户。

当管理员为其他人创建帐户时,允许管理员指定新用户帐户所属的角色可能会有所帮助。 在 存储其他用户信息 教程中,我们了解了如何通过添加其他 WizardSteps来自定义 CreateUserWizard。 让我们看看如何向 CreateUserWizard 添加附加步骤,以指定新用户的角色。

CreateUserWizardWithRoles.aspx打开页面并添加名为 RegisterUserWithRoles的 CreateUserWizard 控件。 将控件的 ContinueDestinationPageUrl 属性设置为“~/Default.aspx”。 由于此处的想法是管理员将使用此 CreateUserWizard 控件创建新的用户帐户,因此请将控件的 LoginCreatedUser 属性设置为 False。 此属性 LoginCreatedUser 指定访问者是否以刚刚创建的用户身份自动登录,并且默认为 True。 我们将其设置为 False,因为当管理员创建新帐户时,我们希望他保持以自己身份登录。

接下来,选择“添加/删除 WizardSteps...”在 CreateUserWizard 的智能标记中添加一个新的 WizardStep,将其 ID 设置为 SpecifyRolesStep。 移动 , SpecifyRolesStep WizardStep 使其在“注册新帐户”步骤之后,但在“完成”步骤之前。 将 WizardStepTitle 属性设置为“指定角色”,将其 StepType 属性设置为 Step,将其 AllowReturn 属性设置为 False。

显示“向导步骤集合编辑器”窗口中选定的“指定角色”属性的屏幕截图。

图 11:将“指定角色” WizardStep 添加到 CreateUserWizard (单击以查看全尺寸映像)

更改此更改后,CreateUserWizard 的声明性标记应如下所示:

<asp:CreateUserWizard ID="RegisterUserWithRoles" runat="server" 
     ContinueDestinationPageUrl="~/Default.aspx" LoginCreatedUser="False"> 

     <WizardSteps> 
          <asp:CreateUserWizardStep ID="CreateUserWizardStep1" runat="server"> 
          </asp:CreateUserWizardStep> 
          <asp:WizardStep ID="SpecifyRolesStep" runat="server" StepType="Step" 

               Title="Specify Roles" AllowReturn="False"> 
          </asp:WizardStep> 
          <asp:CompleteWizardStep ID="CompleteWizardStep1" runat="server"> 
          </asp:CompleteWizardStep> 
     </WizardSteps> 

</asp:CreateUserWizard>

在“指定角色” WizardStep中,添加名为 的 RoleListCheckBoxList。 此 CheckBoxList 将列出可用角色,使访问页面的人员能够检查新创建的用户所属的角色。

我们还有两个编码任务:首先,必须使用系统中的角色填充 RoleList CheckBoxList;第二,当用户从“指定角色”步骤移动到“完成”步骤时,需要将创建的用户添加到所选角色。 我们可以在事件处理程序中 Page_Load 完成第一个任务。 以下代码在首次访问页面时以编程方式引用 RoleList CheckBox,并将系统中的角色绑定到该页面。

protected void Page_Load(object sender, EventArgs e) 
{ 
     if (!Page.IsPostBack) 
     { 
          // Reference the SpecifyRolesStep WizardStep 
          WizardStep SpecifyRolesStep = RegisterUserWithRoles.FindControl("SpecifyRolesStep") as WizardStep; 

          // Reference the RoleList CheckBoxList 
          CheckBoxList RoleList = SpecifyRolesStep.FindControl("RoleList") as CheckBoxList; 

          // Bind the set of roles to RoleList 
          RoleList.DataSource = Roles.GetAllRoles(); 
          RoleList.DataBind(); 
     } 
}

上面的代码应该看起来很熟悉。 在 存储其他用户信息 教程中,我们使用两 FindControl 个 语句从自定义 WizardStep中引用 Web 控件。 将角色绑定到 CheckBoxList 的代码是从本教程前面部分获取的。

为了执行第二个编程任务,我们需要知道“指定角色”步骤何时完成。 回想一下,CreateUserWizard 有一个 ActiveStepChanged 事件,每当访问者从一个步骤导航到另一个步骤时,该事件都会触发。 在这里,我们可以确定用户是否已到达“完成”步骤;如果是这样,我们需要将用户添加到所选角色。

ActiveStepChanged 事件创建事件处理程序并添加以下代码:

protected void RegisterUserWithRoles_ActiveStepChanged(object sender, EventArgs e) 
{ 
     // Have we JUST reached the Complete step? 
     if (RegisterUserWithRoles.ActiveStep.Title == "Complete") 
     { 
          // Reference the SpecifyRolesStep WizardStep 
          WizardStep SpecifyRolesStep = RegisterUserWithRoles.FindControl("SpecifyRolesStep") as WizardStep; 

          // Reference the RoleList CheckBoxList 
          CheckBoxList RoleList = SpecifyRolesStep.FindControl("RoleList") as CheckBoxList; 

          // Add the checked roles to the just-added user 
          foreach (ListItem li in RoleList.Items) 

          { 
               if (li.Selected) 
                    Roles.AddUserToRole(RegisterUserWithRoles.UserName, li.Text); 
          } 
     } 
}

如果用户刚刚到达“已完成”步骤,则事件处理程序会枚举 CheckBoxList 的 RoleList 项,并将刚刚创建的用户分配给所选角色。

通过浏览器访问此页面。 CreateUserWizard 中的第一步是标准的“注册新帐户”步骤,它提示输入新用户的用户名、密码、电子邮件和其他密钥信息。 输入信息以创建名为 Wanda 的新用户。

创建名为 Wanda 的新用户

图 12:创建名为 Wanda 的新用户 (单击以查看全尺寸图像)

单击“创建用户”按钮。 CreateUserWizard 在内部调用 Membership.CreateUser 方法,创建新的用户帐户,然后转到下一步“指定角色”。此处列出了系统角色。 选中“主管”复选框,然后单击“下一步”。

使万达成为监督员角色的成员

图 13:使万达成为主管角色的成员 (单击以查看全尺寸图像)

单击“下一步”会导致回发, ActiveStep 并将 更新为“完成”步骤。 在 ActiveStepChanged 事件处理程序中,将最近创建的用户帐户分配给“监督员”角色。 若要验证这一点,请返回到页面, UsersAndRoles.aspx 然后从 RoleList DropDownList 中选择“监督器”。 如图 14 所示,主管现在由三个用户组成:布鲁斯、蒂托和万达。

布鲁斯、蒂托和万达都是主管

图 14:布鲁斯、蒂托和万达都是主管 (单击查看全尺寸图像)

摘要

角色框架提供用于检索特定用户角色信息的方法,以及用于确定哪些用户属于指定角色的方法。 此外,有多种方法可用于向一个或多个角色添加和删除一个或多个用户。 在本教程中,我们仅重点介绍以下两种方法: AddUserToRoleRemoveUserFromRole。 其他变体旨在将多个用户添加到单个角色,并将多个角色分配给单个用户。

本教程还介绍了如何扩展 CreateUserWizard 控件以包含 以 WizardStep 指定新创建用户的角色。 此步骤可帮助管理员简化为新用户创建用户帐户的过程。

此时,我们已了解如何创建和删除角色,以及如何在角色中添加和删除用户。 但是,我们尚未研究如何应用基于角色的授权。 在以下 教程中 ,我们将介绍如何按角色定义 URL 授权规则,以及如何根据当前登录用户的角色限制页面级功能。

编程快乐!

深入阅读

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

关于作者

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

特别感谢...

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