创建和管理角色
本文档是 Visual C# 教程 (转至 Visual Basic 教程 ) 。
本教程探讨配置 Roles 框架的必需步骤。之后,我们将构建创建和删除角色的网页。
简介在《基于用户的授权 》教程中,我们考察了使用URL 授权来限制某些用户对一组页面的访问,探讨了通过声明和编码方法来根据访问用户调整ASP.NET 页面的功能。不过,这种逐个用户地授予页面访问或功能使用权限的方式在以下这些场合可能会变成一场管理梦魇:用户帐户很多、用户权限经常改变。每当用户获得或失去执行特定任务的授权时,管理员都要更改相应的URL 授权规则、声明标记以及代码。 我们可将用户划分为不同的组即角色,然后按角色来授权,这么做通常就可弥补上述问题。下面我们举例来说明这点。大多数Web 应用程序都有一系列的页面或任务仅供管理用户使用。利用《基于用户的授权》教程中讲的技术,我们要添加相应的URL 授权规则,声明标记以及代码来允许指定的用户帐户执行管理任务。但如果新添加了一个管理员或需要废除一个现有管理员的管理权限,我们只得退回去更改配置文件和网页了。而利用角色情况就不同了,我们可以创建一个名为Administrators 的角色,将信任的用户分配给这个角色。然后,我们可以添加相应的URL 授权规则、声明标记和代码,允许 Administrators 角色执行各种管理任务。有了这个基础架构,不管是添加新的管理员还是删除现有的管理员都是很简单的一件事,只要将用户放入Administrators 角色中或从 Administrators 角色中删除用户即可,不需要更改配置文件、声明标记或代码。 ASP.NET 提供了一个Roles 框架以定义角色并将角色与用户帐户相关联。有了Roles 框架,我们可以创建和删除角色、将用户添加到某个角色或从某个角色中删除用户、确定属于某个角色的所有用户、断定某个用户是否属于一个特定角色。一旦配置好Roles 框架,我们就可以通过 URL 授权规则按角色限制对页面的访问、根据当前登录用户的角色在页面上显示或隐藏额外的信息或功能。 本教程详细介绍配置Roles 框架的必需步骤。然后,我们会构建创建和删除角色的网页。在《为用户指定角色 》教程中,我们会考察怎样将用户添加到角色中以及怎样从角色中删除用户。在《基于角色的授权 》教程中,我们将考察怎样按角色限制对页面的访问,以及怎样根据访问用户的角色调整页面功能。 让我们开始吧 ! 步骤1 :添加新的ASP.NET 页面在本教程以及后面两个教程中,我们将考察与角色有关的各种功能。我们将需要一系列的ASP.NET 页面来实现这些教程中讲述的主题。下面我们创建这些页面并更新站点地图。 首先 , 在项目中创建一个新的名为 Roles 的 文件夹。 然后,添加四个新的页面,将每个页面与Site.master 母版页相关联。将这些页面分别命名为:
此时,项目的解决方案资源管理器看起来应类似图1 中的屏幕快照。 图1 :Roles 文件夹中新添了四个页面(单击此处查看实际大小的图像 ) 每个页面此时都应该有两个内容控件,分别对应母版页的两个ContentPlaceHolder 控件:MainContent 和 LoginContent 。
我们记得,LoginContent ContentPlaceHolder 的默认标记显示一个网站登录或注销链接。而在ASP.NET 页面中存在的这个Content2 内容控件覆盖了母版页的这个默认标记。如《表单身份验证概述 》教程所述,当我们不想在页面左栏中显示与登录与关的选项时,就可以用覆盖掉默认标记这一方法。 不过对这四个页面而言,我们想显示出母版页的LoginContent ContentPlaceHolder 默认标记。因此,在这里我们删除Content2 内容控件的声明标记。完成后,这四个页面的标记中里就只包含一个内容控件了。 最后,我们来更新站点地图 (Web.sitemap) ,使其包括这些新的网页。. 我们在有关成员资格的教程中添加了<siteMapNode> ,现在在其后面添加如下的XML 。
更新网站地图后,通过浏览器访问该站点。如图2 所示,左边的导航栏现在包括这几个页面的条目了。 图2 :Roles 文件夹中新添了四个页面(单击此处查看实际大小的图像 ) 步骤2 :指定并配置Roles 框架使用的提供者和Membership 框架一样,Roles 框架也是建立于提供者模式之上。如《安全基础和ASP.NET 支持 》教程所述,.NET Framework 随带有三个内置的Roles 提供者:AuthorizationStoreRoleProvider 、WindowsTokenRoleProvider 和SqlRoleProvider 。本系列教程专注于SqlRoleProvider ,它用一个Microsoft SQL Server 数据库作为角色存储。 本质上,Roles 框架和SqlRoleProvider 一起工作的方式如同 Membership 框架和 SqlMembershipProvider 。.NET Framework 包含一个Roles 类,该类用作Roles 框架的 API 。Roles 类中有一些静态方法,比如 CreateRole 、DeleteRole 、GetAllRoles 、AddUserToRole 、IsUserInRole 等等。当调用这些方法时,Roles 类将调用委托给配置好的提供者来处理。作为响应,SqlRoleProvider 处理针对角色的表(比如:aspnet_Roles 和aspnet_UsersInRoles )。 为了在我们的应用程序中使用SqlRoleProvider 提供者,我们需要指定使用哪个数据库作为角色存储。而SqlRoleProvider 要求指定的角色存储包含某些数据库表、视图以及存储过程。我们可以使用aspnet_regsql.exe 工具 来添加这些必需的数据库对象。现在我们已经有了这么一个数据库,它具有SqlRoleProvider 需要的schema 。在前面的《在SQL Server 中创建Membership Schema 》教程中,我们创建了一个名为SecurityTutorials.mdf 的数据库,并使用aspnet_regsql.exe 添加了一些应用程序服务,其中就包含了SqlRoleProvider 需要的数据库对象。因此,我们只需要告诉Roles 框架启用角色支持并且使用SqlRoleProvider ,令这个提供者使用SecurityTutorials.mdf 作为角色存储即可。 对 Roles 框架的配置通过应用程序的 Web.config 文件中的 <roleManager> 元素来进行。默认情况下,角色支持是禁用的。要启用该功能,我们须将<roleManager> 元素的 enabled 属性设置为 true ,如下所示:
默认情况下,所有 Web 应用程序都有一个类型为 SqlRoleProvider 、名为 AspNetSqlRoleProvider 的 Roles 提供者。该默认的提供者注册于 machine.config 文件(位于 %WINDIR%\Microsoft.Net\Framework\v2.0.50727\CONFIG ):
该提供者的 connectionStringName 属性指定了所使用的角色存储。对于提供者AspNetSqlRoleProvider ,此属性设置为了 LocalSqlServer ,LocalSqlServer 也是在machine.config 中定义的,默认时指向 App_Data 文件夹下的一个名为 aspnet.mdf 的 SQL Server 2005 Express Edition 数据库。 因此,如果我们只是启用 Roles 框架而没有在Web.config 文件中指定任何提供者信息,应用程序将使用默认注册的Roles 提供者 AspNetSqlRoleProvider 。如果 ~/App_Data/aspnet.mdf 数据库不存在,ASP.NET 运行时会自动创建这个数据库并添加应用程序服务schema 。不过,我们不想使用 aspnet.mdf 数据库,而是要使用 SecurityTutorials.mdf 数据库,这个数据库我们已经创建好了而且对其添加了应用程序服务schema 。我们可以通过两种方法来完成这一修改:
在 Web.config 文件中添加如下的 Roles 配置标记。这里注册了一个新的名为 SecurityTutorialsSqlRoleProvider 的提供者。
上述标记指定SecurityTutorialsSqlRoleProvider 为默认的提供者(通过<roleManager> 元素中的 defaultProvider 属性)。同时还将 SecurityTutorialsSqlRoleProvider 的applicationName 属性设置为SecurityTutorials ,这个设置与 Membership 提供者(SecurityTutorialsSqlMembershipProvider) 使用的 applicationName 设置是相同的。虽然这里没有显示出来,但SqlRoleProvider 的 <add> 元素 还可以包含一个 commandTimeout 属性,用于指定数据库的超时时间,其单位为秒。其默认值为30 。 完成上述配置后,我们就可以在应用程序中使用角色功能了。 注意 : 上述配置标记演示了<roleManager> 元素的enabled 和defaultProvider 属性的用法,. 另外还有许多属性可以影响Roles 框架按用户关联角色信息的方式。我们将在《基于角色的授权 》教程中探讨这些属性设置。 步骤3 :探讨Roles API通过 Roles 类 可以访问 Roles 框架的功能,该类有十三个静态方法可执行基于角色的操作。当我们在步骤4 和步骤 6 中考察怎样创建和删除角色的时候,我们将用到CreateRole 和 DeleteRole 方法,前者向系统添加角色,后者从系统中删除角色。 要获取系统中所有角色的列表,可使用GetAllRoles 方法 (参见步骤 5 )而RoleExists 方法 会返回一个布尔值,指示某个指定的角色是否存在。. 在下一教程中我们将探讨怎样将用户和角色关联起来。Roles 类的 AddUserToRole 、AddUserToRoles 、AddUsersToRole 和 AddUsersToRoles 方法用于将一个或多个用户添加到一个或多个角色中。要将用户从角色中删除,可使用RemoveUserFromRole 、RemoveUserFromRoles 、RemoveUsersFromRole 或 RemoveUsersFromRoles 方法。 在《基于角色的授权 》教程中,我们将考察怎样通过编程根据当前登录用户的角色显示或隐藏功能。为此,我们可以使用FindUsersInRole 、GetRolesForUser 、GetUsersInRole 或IsUserInRole 方法。 注意 : 要记住,无论何时调用这些方法,Roles 类都会将这些调用委托给我们配置的提供者。就我们的程序而言,这意味着调用将传给SqlRoleProvider 。SqlRoleProvider 继而根据调用的是哪个方法来执行相应的数据库操作。例如,调用 Roles.CreateRole("Administrators") 会使 SqlRoleProvider 执行 aspnet_Roles_CreateRole 存储过程,该过程向 aspnet_Roles 表中添加一个新的名为 “Administrators” 的记录。 本教程其余部分将考察怎样使用Roles 类的 CreateRole 、GetAllRoles 和 DeleteRole 方法来管理系统中的角色。 步骤4 :创建新角色利用角色,我们可以任意地将用户分组,这种分组常常是为了便于实施授权规则。但为了用角色作为授权机制,我们首先要指定在应用程序中存在哪些角色。 遗憾的是,ASP.NET 没有 CreateRoleWizard 控件。为了向系统添加新的角色,我们需要创建一个合适的用户界面并调用Roles API 。可喜的是这很容易实现。 注意 : 虽然没有CreateRoleWizard Web 控件,但我们有ASP.NET Web Site Administration Tool ,该工具是个本地ASP.NET 应用程序,旨在帮助我们查看和管理自己的Web 应用程序的配置。. 不过,我对 ASP.NET Web Site Administration Tool 不太感兴趣,原因有二:其一,这个工具的缺陷多了点,而且其用户体验不甚理想。其二,这个工具的设计初衷是本地运行,这意味着如果我们需要通过一个活动网站远程管理角色,就只能构建立自己的角色管理网页了。由于这两个原因,在本教程和下一教程中,我们将关注在一个网页中构建必要的角色管理工具,而不是依赖于ASP.NET Web Site Administration Tool 。 打开 Roles 文件夹下的 ManageRoles.aspx 页面,在其中添加一个文本框控件和一个Web 按钮控件。分别将文本框控件的ID 属性设置为 RoleName ,按钮控件的 ID 和 Text 属性设置为 CreateRoleButton 和 “Create Role” 。此时页面的声明标记应类似如下:
然后,在设计器中双击CreateRoleButton 按钮控件创建一个 Click 事件处理程序,在其中添加如下代码:
上述代码首先将 RoleName 文本框中输入的角色名称经类型转换(Trim) 后赋值给 newRoleName 变量 。然后调用Roles 类的 RoleExists 方法确定系统中是否已经存在newRoleName 角色。如果不存在就调用 CreateRole 方法创建该角色。 如果传递给CreateRole 方法的角色名称早已存在于系统中,就会抛出一个ProviderException 。所以该代码首先要进行检查,确保角色不在系统中后再调用CreateRole 。Click 事件处理程序最后以清空 RoleName 文本框的 Text 属性而结束。 注意 : 你可能会好奇,如果在 RoleName 文本框中不输入任何内容将会怎样呢?如果传给CreateRole 方法的参数值为 null 或一个空字符串,将会引发异常。同样,如果输入的角色名称包含逗号,也会引发异常。因此,我们应在页面上添加验证控件,以确保用户确实输入了角色名称并且该角色名称不含任何逗号。 我将此作为练习留给读者完成。 现在我们来创建一个名为 “Administrators” 的角色。通过浏览器访问 ManageRoles.aspx 页面,在文本框中键入 “Administrators” (参见图 3 ),然后单击 “Create Role” 按钮。 图3 :创建一个 “Administrators” 角色(单击此处查看实际大小的图像 ) 此时发生了什么 ?此时页面回传,但页面上没有相关提示可让我们直观地了解到这个角色实际已成功创建。我们将在步骤5 中更新此页面,使其可提供直观的反馈。而当前,您可以进入到SecurityTutorials.mdf 数据库中,显示aspnet_Roles 表中的数据,从而验证刚才已成功创建了这个角色。如图4 所示,aspnet_Roles 表包含一条刚才添加的 Administrators 角色的记录。 图4 :aspnet_Roles 表中有一条 “Administrators” 记录(单击此处查看实际大小的图像 ) 步骤5 :显示系统中的角色我们来对 ManageRoles.aspx 页面进行扩充,使其罗列出系统中当前的角色。 为此,在页面中添加一个 GridView 控件 ,将其ID 属性设置为 RoleList 。然后,在页面的代码隐藏类中添加一个名为DisplayRolesInGrid 的方法,代码如下:
上述代码调用 Roles 类的 GetAllRoles 方法,以字符串数组的形式返回系统中所有的角色。然后将这个字符串数组绑定到GridView 。为了在首次加载页面时就将角色列表绑定到GridView ,我们要在页面的 Page_Load 事件处理程序中调用DisplayRolesInGrid 方法。下面的代码在首次访问页面时而不是在后续回传时调用这个方法。
编写好上述代码后,通过浏览器访问该页面。 如图 5 所示,会看到一个标为 “Item” 的网格,该网格只有一列。该网格只有一行,就是我们在步骤4 中添加的角色 Administrators 。 图5 :GridView 在仅有的列中显示角色(单击此处查看实际大小的图像 ) GridView 只显示一列(标为“Item” )的原因是,GridView 的 AutoGenerateColumns 属性设置为了 True (默认值),这使得 GridView 会自动为其数据源中的每个属性创建一个列。而一个数组只有一个属性,即表示数组元素的属性,因此在GridView 中只有一列。 用GridView 显示数据时,我喜欢显式地定义出我的各个列,而不喜欢让GridView 隐式地生成它们。 通过显式方式定义各列,我们能更轻松地格式化数据、重排各列以及执行其它常见的任务。因此,我们来更新GridView 的声明标记,以显式地定义它的各列。 首先将GridView 的 AutoGenerateColumns 属性设置为 False 。然后在网格中添加一个TemplateField ,将其HeaderText 属性设置为 “Roles” ,将其 ItemTemplate 配置为显示数组的内容。为此,将一个名为RoleNameLabel 的Web 标签控件添加到 ItemTemplate 中,将该控件的 Text 属性绑定到 Container.DataItem 。 这些属性以及 ItemTemplate 的内容可通过声明方式或 GridView 的 Fields 对话框和 Edit Templates 界面来设置。要进入 Fields 对话框,单击GridView 的智能标记中的 “Edit Columns” 链接。然后,取消选中 “Auto-generate fields” 复选框,以使 AutoGenerateColumns 属性设置为 False ,在GridView 中添加一个 TemplateField ,将其 HeaderText 属性设置为 “Role” 。要定义ItemTemplate 的内容,从 GridView 的智能标记中选择 “Edit Templates” 选项。将一个 Web 标签控件拖放到 ItemTemplate 中,设置其 ID 属性为RoleNameLabel ,配置其数据绑定设置项,使其 Text 属性绑定到Container.DataItem 。 不管使用的是哪种方式,完成之后,GridView 最终的声明标记应类似如下:
注意 : 我们是通过绑定语法 <%# Container.DataItem %> 来将数组的内容显示出来的。为何在显示绑定到GridView 的数组的内容时使用该语法?对这个问题的详解超出了本教程的范畴,可参阅将标量数组绑定到 Web 数据控件 来了解有关此问题的详情。 目前,RoleList GridView 只是在首次访问页面时才绑定到角色列表,而每当添加了新角色时,我们都需要刷新网格。为此,更新CreateRoleButton 按钮的Click 事件处理程序,使其在创建了新角色时调用DisplayRolesInGrid 方法。
现在,当用户添加了新角色时,RoleList GridView 会在页面回传时显示出新添加的角色,同时给出一个直观的反馈,指示该角色已成功创建。下面我们举例说明。通过浏览器访问ManageRoles.aspx 页面,添加一个各为 “Supervisors” 的角色。单击 “Create Role” 按钮,继而页面回传,网格会更新,其中包含了Administrators 和新添的角色,Supervisors 。 图6 :Supervisors 角色添加进来(单击此处查看实际大小的图像 ) 步骤6 :删除角色至此,用户可以在 ManageRoles.aspx page 页面中创建新角色并查看所有现有的角色。下面我们来让用户可以删除角色。Roles.DeleteRole 方法有两个重载:
如果 roleName 为 null 或空字符串,或者 roleName 含有逗号,DeleteRole 方法也会抛出异常。如果系统中不存在这个roleName ,DeleteRole 方法将执行失败而无提示,不会抛出异常。. 下面我们对ManageRoles.aspx 页面中的 GridView 进行扩充,使其包含一个 Delete 按钮,而单击该按钮会删除选中的角色。首先将一个Delete 按钮添加到 GridView 中:进入 Fields 对话框,添加一个 Delete 按钮,从 CommandField 选项下可找到这个按钮。使 Delete 按钮成为最左边的列,将其 DeleteText 属性设置为 “Delete Role” 。 图7 :将一个 Delete 按钮添加到 RoleList GridView (单击此处查看实际大小的图像 ) 添加了 Delete 按钮后,GridView 的声明标记应类似如下:
接下来 , 为GridView 的 RowDeleting 事件创建 一个事件处理程序 。 在我们单击“Delete Role” 按钮,页面回传时触发的正是这个事件。 将以下代码添加到这个事件处理程序中:
代码首先编码引用其 “Delete Role” 按钮被单击的那一行的 RoleNameLabel Web 控件。然后调用 Roles.DeleteRole 方法,传入 RoleNameLabel 的Text 值和一个 false 值,这样不论是否有任何的用户与该角色相关联都删除该角色。最后,刷新RoleList GridView ,这样刚才删除的角色不再出现于网格中。 注意 : 就这个“Delete Role” 按钮的实现来说,在删除角色前没有要求用户进行任何形式的确认。我们可以通过一个客户端确认对话框来确认一个操作,此为最容易的操作确认方法之一。有关该方法的详情,请参见添加对删除操作的客户端确认 。 小结许多 Web 应用程序都有某些授权规则和页面级功能仅供某些类别的用户来使用。例如,可能有一组网页只能由管理员来访问。与逐个用户地定义这些授权规则相比,按角色来定义这些规则通常更为实用。就是说,比起显式地允许用户Scott 和 Jisun 访问管理网页,一个更易于维护的方法是,允许Administrators 角色的成员访问这些页面,然后将 Scott 和Jisun 标记为属于 Administrators 角色的用户。 有了 Roles 框架,我们可以很容易地创建和管理角色。在本教程中,我们探讨了怎样配置Roles 框架以使用 SqlRoleProvider ,该提供者使用一个 Microsoft SQL Server 数据库作为角色存储。我们还创建了一个网页,该网页可列出系统中所有现有的角色,而且允许我们创建新的角色、删除现有的角色。在后续教程中,我们将考察怎样将用户分配给角色以及怎样进行基于角色的授权。 快乐编程!
|