http://blog.csdn.net/deepbluekk/archive/2006/08/15/1067509.aspx
一、 简介
在本文中,我们将探讨ASP.net 2.0的会员、角色和配置问题。ASP.NET中的会员系统提供了一个可编程API用于创建和管理用户账户,而其中的角色部分使开发者能够定义一组角色并把用户与角色相关联。典型地,一个提供用户账户的网站都具有只为某些用户、认证用户或属于特定角色的用户所存取的内容部分。
例如,一个Web站点可能有一组页面——它允许一个可信任用户编辑该Web站点的内容或管理已有用户。不是简单地试图隐藏这个页面并且希望没有人偶然在浏览它时遇到麻烦,或硬编码授权而仅仅允许单个用户使用;一种更为强壮和安全的方式是定义一个管理员角色——然后赋给他一组选出来的信任用户。最后,这些管理web页面被进一步配置以允许仅那些具有管理员角色的用户可以进行存取操作。同样,该Web站点可能包含一组只有认证用户可以存取的页面。
既然该站点的某些部分可能只能为某些用户所存取,那么,这就使得我们在建立站点导航时有点进退两难。我们是否要包括那些只有授权用户才能在Web站点的站点地图中存取的页面?如果我们这样做了,那么所有的用户将会在该站点的菜单或树视图中看到受限制的页面。为什么不能向存取它们的用户显示指向这些页面的链接呢?如果我们从站点地图上删除了这些受限制的页面,那么那些来观看这些页面的授权用户就无法容易地导航到它们,因为它们不是站点地图的一部分,并因此不会出现在该站点的树视图或菜单中!
值得庆幸的是,ASP.NET 2.0的站点导航提供了一种称为安全性修剪的特性。当用支持安全性修剪的功能获得站点地图信息时,只有那些当前登录的用户具有访问授权的站点地图结点才是可用的。这意味着,站点的TreeView或Menu将仅包含为当前登录用户可存取的那些部分。要想了解怎样配置站点导航以支持安全性修剪,请接着往下读!
二、 配置ASP.NET 2.0的会员和(可选)角色
既然站点导航安全性修剪把站点地图数据基于访问页面的用户和为站点地图中的页面定义的授权设置,那么在我们可以分析安全性修剪之前,你必须首先配置你的Web站点以使用ASP.NET 2.0的会员服务功能。(你也可以配置该站点来使用角色,并使用基于角色的授权优点,但是这对于展示安全性修剪概念并不作要求。)至于详细讨论怎样配置一站点以使用会员和角色已经超出了本文的范围。
如果你不想在一个新的Web站点上花费时间来安装会员和角色特性的话,你可以使用它。具体地说,在本文后面可下载的Web站点中包含两个角色——管理员(Administrator)和测试员(Tester),并有四个用户:
·Superman,其角色为管理员和测试者
·Admin,其角色为管理员
·Mr.Tester,其角色为测试者
·Average User,没有任何角色
而且,我在该工程中有三个文件夹:Admin,Tester和AuthUsersOnly。前两个文件夹已经被配置仅允许具有管理员和测试者角色的用户存取。AuthUsersOnly文件夹被限制仅用于认证的用户。
三、 配置站点导航以使用安全性修剪
默认地,站点导航并不使用安全性修剪。不管什么用户正在访问站点,并且不管定义什么样的授权规则,当每个用户通过一TreeView或菜单Web控件来观看站点地图数据时,他都被可以看到站点地图中的所有部分。通过启动安全性修剪,站点导航系统将自动地基于当前登录的用户和为站点地图中的<siteMapNode>元素所参考的页面的授权而限制显示结果。
可以使用下列模式通过Web.config文件来配置站点导航设置:
<siteMap defaultProvider="XMLSiteMapProvider" enabled="true"> <providers> <add name="XMLSiteMapProvider" description="Default SiteMap provider." type="System.Web.XmlSiteMapProvider" siteMapFile="siteMapFileName" securityTrimmingEnabled="true" /> </providers> </siteMap> |
回想一下在本系列第一部分中的讨论——站点导航系统使用了提供者模型。这个模型提供给开发者一些良好定义的公共API,但是允许(如果需要的话)定制内部实现细节。缺省地,站点导航特点使用XmlSiteMapProvider-它从XML格式化的站点地图文件Web.sitemap中获得站点地图信息。你可以通过Web.config文件来改变所使用的提供者,或修改针对缺省提供者的缺省设置。
为了定制缺省提供者的设置,只要简单地添加一新的提供者-它使用与缺省提供者(System.Web.XmlSiteMapProvider)相同的类型来定制所需要的设置。上面显示的代码片段展示了定制两个XmlSiteMapProvider的设置:
·siteMapFile设置指定由提供者所使用的站点地图文件的文件名;缺省地,这个值是Web.sitemap。你如果喜欢,可以在此定制文件名。但是,我鼓励你确保站点地图文件名以.sitemap扩展名结尾,因为缺省地这个扩展名被
ASP.net引擎保护,从而阻止web访问者观看站点地图文件。
·securityTrimmingEnabled设置显示是否使用安全性修剪。为了使用安全性修剪,可以把它设置为true,如上面所示。
就是这些!仅需作此改变,站点导航系统就会根据当前登录的用户和为在<siteMapNode>元素中的URL定义的授权设置非常聪明地返回正确的部分。下列屏幕快照显示了当分别在匿名访问者(还没有登录的)、普通用户以及Admin用户访问时的TreeView的显示情况。该匿名访问者仅能看到两个链接-Home和 My Blog。普通用户能看到另外的一个链接-Auth Users,而匿名访问者是看不到它的,因为这个结点的URL(~/AuthUsers/Default.aspx)被配置为仅允许认证用户观看。Admin用户还能看到另外其它链接,因为他是管理员角色并且为Admin站点地图结点(~/Admin/Default.aspx)所指向的URL被配置仅允许由管理员存取。
 当由匿名访问者访问时的TreeView
|
 为普通用户访问时的TreeView
|
 为Admin访问时的TreeView
|
四、 用角色属性阻止安全性修剪
可能有一些时候-你想要显式地通知安全性修剪不要为一个特别的角色或一组角色修剪一个站点地图部分。例如,如果你的站点地图包含一个到外部资源的链接,那么该站点导航系统不可能针对这个远程资源确定授权规则。因此,它为所有的用户修剪该结点。也就是说,如果你启动了安全性修剪功能并且在用一外部链接(如<siteMapNode url="http://www.scottonwriting.net/sowBlog/" title="My Blog"/>)使用一个站点地图,那么,没有用户会在TreeView或Menu控件中看到这些。然而,你可能想要让该站点导航系统为那些具有Administrator和Tester角色的用户显示这一结点。(或不考虑任何用户的角色。)
同样,即使他们没有被授权存取该资源,你也可能想向这些用户显示一个局部站点地图结点。例如,一访问该站点却还没有登录的用户肯定不会看到在TreeView中的Admin链接。然而,我们仍可能想显示之。点击它将会使用户被导航到~/Admin/Default.ASPx页面,此时,系统将看到他们没有被认证。这将把他们引导到登录页面。在登录后,他们将被自动地引导回Admin页面。如果他们不是管理员角色,他们将被送回到登录页面,否则他们将被同意存取Admin部分。
为了不修剪针对一个特定的站点地图结点的特别角色,可以使用在相应的<siteMapNode>元素中的角色属性。(注意:这一设置不会应用到descendent<siteMapNode>元素;也就是说,对那些显式地指定另外的应该看到该结点的角色,你必须显式地在每一个<siteMapNode>元素上设置这一属性)。此角色属性能包含一个角色名,一个用逗号间隔列表的角色名字,或一个星号(*)来表示所有的用户。包含在本文末尾的下载文件中的下列站点地图文件显示出怎样使用角色属性来使得所有的用户拥有一个外部的站点地图结点参考。(可以联想到,当启动安全性修剪功能时,如果在此忽略角色属性,这将导致不向任何用户显示这个站点地图结点。)>
<siteMap XMLns="http://schemas.microsoft.com/AspNet/SiteMap-File-1.0" > <siteMapNode url="~/Default.aspx" title="Home"> <siteMapNode url="~/About.aspx" title="About" /> <siteMapNode url="~/Admin/Default.aspx" title="Admins" /> <siteMapNode url="~/Tester/Default.aspx" title="Tester" /> <siteMapNode url="~/AuthUsers/Default.aspx" title="Auth Users Only" /> <!-- For links to outside resources, need to explicitly define what roles should be shown this section --> <siteMapNode url="http://www.scottonwriting.net/sowBlog/" title="My Blog" roles="*" /> </siteMapNode> </siteMap> |
该角色属性还可以用于在安全性修剪功能性上增加一些性能改进。在启动了安全性修剪功能后,站点导航提供者为所有的定义在站点地图中的结点自动地检查授权规则。对于那些你想要向所有的用户显示的结点,通过添加roles="*",你可以绕过这一检查,如上例中所示。通过添加这个属性,你会短路正常的授权检查,从而改进安全性修剪性能。
五、 结论 除了提供站点导航支持外,
ASP.NET 2.0还使得构建包括用户帐户支持和基于角色的授权的
Web站点非常容易。随之而来的并不令人感到惊奇-这两个系统可以互操作并且提供一个站点地图-其返回内容是基于当前登录的用户和定义在站点地图中的URL的授权设置。配置站点导航以限制基于访问用户和授权设置的结果就象把一些代码添加Web.config文件一样地容易。
http://www.dotnetjunkies.com/QuickStartv20/aspnet/doc/ctrlref/navigation/treeview.aspx
<%@ Page Language="C#" %>
<head runat="server"/>
<h2>Customizing TreeView Styles</h2>
<form runat="server">
<asp:TreeView
RootNodeStyle-ImageUrl="~/images/xp/computer.gif"
ParentNodeStyle-ImageUrl="~/images/xp/folder.gif"
LeafNodeStyle-ImageUrl="~/images/xp/ie.gif"
NodeIndent="20"
runat="server"
>
<NodeStyle Font-Name="Arial" Font-Size="8pt" ForeColor="DarkBlue" HorizontalPadding="5"/>
<RootNodeStyle Font-Bold Font-Size="9pt"/>
<HoverNodeStyle Font-UnderLine/>
<Nodes>
<asp:TreeNode Text="My Computer" Expanded="true">
<asp:TreeNode Text="Favorites" ImageUrl="~/images/xp/star.gif">
<asp:TreeNode Text="News">
<asp:TreeNode Text="MSN" NavigateUrl="http://www.msn.com"/>
<asp:TreeNode Text="MSNBC News" NavigateUrl="http://www.msnbc.msn.com"/>
</asp:TreeNode>
<asp:TreeNode Text="Technology">
<asp:TreeNode Text="Microsoft" NavigateUrl="http://www.microsoft.com"/>
<asp:TreeNode Text="ASP.NET" NavigateUrl="http://www.asp.net"/>
<asp:TreeNode Text="GotDotNet" NavigateUrl="http://www.gotdotnet.com"/>
<asp:TreeNode Text="MSDN" NavigateUrl="http://msdn.microsoft.com"/>
</asp:TreeNode>
<asp:TreeNode Text="Shopping">
<asp:TreeNode Text="MSN Shopping" NavigateUrl="http://shopping.msn.com"/>
<asp:TreeNode Text="MSN Autos" NavigateUrl="http://autos.msn.com"/>
</asp:TreeNode>
</asp:TreeNode>
<asp:TreeNode Text="City Links">
<asp:TreeNode Text="MapPoint" NavigateUrl="http://www.mappoint.com"/>
<asp:TreeNode Text="MSN City Guides" NavigateUrl="http://local.msn.com"/>
</asp:TreeNode>
<asp:TreeNode Text="Music Links">
<asp:TreeNode Text="MSN Music" NavigateUrl="http://music.msn.com"/>
</asp:TreeNode>
</asp:TreeNode>
</Nodes>
</asp:TreeView>
</form>
<!--/////////////////////////////////////////-->
<%@ Page Language="C#" Theme="Default" %>
<head runat="server"/>
<script runat="server">
void MyTree_Select (Object source, EventArgs e) {
String value = MyTree.SelectedNode.Value;
String path = MyTree.SelectedNode.ValuePath;
Response.Write ("The value selected was " + value + "<br>");
Response.Write ("The value path is " + path + "<br>");
}
</script>
<body bgcolor="#eeeeee">
<h2>TreeView Selection</h2>
<form runat="server">
<asp:TreeView Id="MyTree" SkinId="MSDN"
OnSelectedNodeChanged="MyTree_Select"
ExpandDepth="0"
runat="server"
>
<SelectedNodeStyle BackColor="White" BorderWidth="1"/>
<Nodes>
<asp:TreeNode Text="Welcome to Code Center" Value="1.0" />
<asp:TreeNode Text="Data Binding" Value="2.0" SelectAction="Expand">
<asp:TreeNode Text="ANCHOR Element" Value="2.1"/>
<asp:TreeNode Text="APPLET Element" Value="2.2"/>
<asp:TreeNode Text="BUTTON Element" Value="2.3" SelectAction="Expand">
<asp:TreeNode Text="Text Attribute" Value="2.3.1"/>
<asp:TreeNode Text="Value Attribute" Value="2.3.2"/>
</asp:TreeNode>
</asp:TreeNode>
<asp:TreeNode Text="Data Source Objects" Value="3.0" SelectAction="Expand">
<asp:TreeNode Text="Array (VB)" Value="3.1"/>
<asp:TreeNode Text="Calendar Applet (Java)" Value="3.2"/>
<asp:TreeNode Text="Dual DSO (HTML)" Value="3.3"/>
<asp:TreeNode Text="Loan Calculator (VB)" Value="3.4"/>
</asp:TreeNode>
<asp:TreeNode Text="DirectX Transformations" Value="4.0" SelectAction="Expand">
<asp:TreeNode Text="Settings Reference" Value="4.1"/>
</asp:TreeNode>
<asp:TreeNode Text="Content & Component Delivery" Value="5.0" SelectAction="Expand">
<asp:TreeNode Text="Channel" Value="5.1"/>
<asp:TreeNode Text="IISLog" Value="5.2"/>
</asp:TreeNode>
<asp:TreeNode Text="Common Controls" Value="6.0" SelectAction="Expand">
<asp:TreeNode Text="Rebar Control" Value="6.1"/>
<asp:TreeNode Text="Virtual List View Control" Value="6.2"/>
</asp:TreeNode>
<asp:TreeNode Text="Component Development" Value="7.0" SelectAction="Expand">
<asp:TreeNode Text="Band Object Implementation" Value="7.1"/>
</asp:TreeNode>
<asp:TreeNode Text="Internet Explorer 5 Demos" Value="8.0" SelectAction="Expand">
<asp:TreeNode Text="Ad Banner" Value="8.1"/>
</asp:TreeNode>
<asp:TreeNode Text="Internet Explorer 5.5 Demos" Value="9.0" SelectAction="Expand">
<asp:TreeNode Text="Binary Behavior" Value="9.1"/>
</asp:TreeNode>
<asp:TreeNode Text="Internet Explorer 6 Demos" Value="10.0" SelectAction="Expand">
<asp:TreeNode Text="HTML+TIME Transitions" Value="10.1"/>
<asp:TreeNode Text="HTMLEditor" Value="10.2"/>
</asp:TreeNode>
<asp:TreeNode Text="Messaging & Collaboration" Value="11.0" SelectAction="Expand">
<asp:TreeNode Text="WAB Property Inspector Tool" Value="11.1"/>
</asp:TreeNode>
<asp:TreeNode Text="Windows Media" Value="12.0" SelectAction="Expand">
<asp:TreeNode Text="Simple ASX" Value="12.1"/>
</asp:TreeNode>
</Nodes>
</asp:TreeView>
</form>
</body>
<!--/////////////////////// 遍历目录下所有文件 /////////////////////////////-->
<%@ Page Language="C#" Theme="Default" %>
<%@ Import Namespace="System.IO" %>
<html>
<head runat="server" />
<script language="C#" runat="server">
static readonly char[] _slashArray = new char[] {'/'};
void PopulateNode(Object source, TreeNodeEventArgs e)
{
TreeNode node = e.Node;
if (node.Value == "Demos")
node.Value = "~/";
String rootDirectory = Request.MapPath("~/", Request.ApplicationPath, false);
String fullPath = Request.MapPath(node.Value, Request.ApplicationPath, false);
if (fullPath.StartsWith(rootDirectory) == false)
{
// Mitigate against spoofed callback arguments
// Requested directory is not under root directory
return;
}
String[] dirs = Directory.GetDirectories(fullPath);
// Enumerate directories
foreach (String dir in dirs)
{
String virtualDir = node.Value.TrimEnd(_slashArray) + "/" + Path.GetFileName(dir);
TreeNode newNode = new TreeNode(Path.GetFileName(dir), virtualDir);
if (Directory.GetFiles(dir).Length > 0
|| Directory.GetDirectories(dir).Length > 0)
{
newNode.PopulateOnDemand = true;
}
node.ChildNodes.Add(newNode);
}
// Enumerate files
String[] files = Directory.GetFiles(fullPath);
foreach (String file in files)
{
TreeNode newNode = new TreeNode(Path.GetFileName(file), Path.GetFileName(file));
node.ChildNodes.Add(newNode);
}
}
</script>
<body>
<form runat="server">
<h2>
Populating TreeView Nodes On-Demand</h2>
<asp:TreeView ID="MyTree" SkinID="Explorer" PathSeparator="|" OnTreeNodePopulate="PopulateNode"
ExpandDepth="1" runat="server">
<Nodes>
<asp:TreeNode Text="Demos" PopulateOnDemand="true" />
</Nodes>
</asp:TreeView>
</form>
</body>
</html>
<!--****************************** 数据库应用 *****************************-->
<%@ Page Theme="Default" %>
<html>
<head runat="server"/>
<script language="C#" runat="server">
void GetProductCategories (TreeNode node) {
CategoryList categories = WarehouseDB.GetProductCategories();
foreach (Category c in categories) {
TreeNode newNode = new TreeNode(c.Name, c.Id);
newNode.SelectAction = TreeNodeSelectAction.Expand;
newNode.PopulateOnDemand = true;
node.ChildNodes.Add(newNode);
}
}
void GetProductsForCategory (TreeNode node) {
String categoryId = node.Value;
ProductList products = WarehouseDB.GetProductsForCategory(categoryId);
foreach (Product p in products) {
TreeNode newNode = new TreeNode(p.Name, p.Id);
node.ChildNodes.Add(newNode);
}
}
void PopulateNode (Object source, TreeNodeEventArgs e) {
switch (e.Node.Depth) {
case 0 :
GetProductCategories(e.Node);
break;
case 1 :
GetProductsForCategory(e.Node);
break;
}
}
</script>
<body>
<h2>Populating TreeView Nodes from a SQL Database</h2>
<form runat="server">
<asp:TreeView OnTreeNodePopulate="PopulateNode" SkinId="Simple" Width="250" ExpandDepth="0"
runat="server">
<Nodes>
<asp:TreeNode Text="Inventory" SelectAction="Expand" PopulateOnDemand="true"/>
</Nodes>
</asp:TreeView>
</form>
</body>
</html>
<!--**************************** 绑定 XML 文件 ****************************-->
<%@ Page Language="C#" Theme="Default" %>
<html>
<body>
<form runat="server">
<asp:XmlDataSource id="MySource" DataFile="~/App_Data/Bookstore.xml" runat="server"/>
<asp:TreeView
SkinId="Bookstore"
DataSourceId="MySource"
ExpandDepth="3"
MaxDataBindDepth="3"
runat="server"
/>
</form>
</body>
</html>
<!--//XML文件-->
<Bookstore>
<genre name="Business">
<book ISBN="BU1032" Title="The Busy Executive's Database Guide" Price="19.99">
<chapter num="1" name="Introduction">
Abstract...
</chapter>
<chapter num="2" name="Body">
Abstract...
</chapter>
<chapter num="3" name="Conclusion">
Abstract...
</chapter>
</book>
<book ISBN="BU2075" Title="You Can Combat Computer Stress!" Price="2.99">
<chapter num="1" name="Introduction">
Abstract...
</chapter>
<chapter num="2" name="Body">
Abstract...
</chapter>
<chapter num="3" name="Conclusion">
Abstract...
</chapter>
</book>
<book ISBN="BU7832" Title="Straight Talk About Computers" Price="19.99">
<chapter num="1" name="Introduction">
Abstract...
</chapter>
<chapter num="2" name="Body">
Abstract...
</chapter>
<chapter num="3" name="Conclusion">
Abstract...
</chapter>
</book>
</genre>
</Bookstore>
<!--**********************绑定 SiteMap ******************************-->
<?xml version="1.0" encoding="utf-8" ?>
<siteMap>
<siteMapNode title="ASP.NET Tutorials" url="~/default.aspx" >
<siteMapNode title="Page Development" url="~/pages.aspx">
<siteMapNode title="Master Pages" url="~/masterpages.aspx"/>
<siteMapNode title="Themes and Styles" url="~/themes.aspx"/>
<siteMapNode title="Site Navigation" url="~/sitenav.aspx"/>
<siteMapNode title="Client Side Script" url="~/client.aspx"/>
</siteMapNode>
<siteMapNode title="Developer Infrastructure" url="~/developer.aspx">
<siteMapNode title="Membership and Roles" url="~/membership.aspx"/>
<siteMapNode title="Personalization" url="~/personalization.aspx"/>
<siteMapNode title="Profile" url="~/profile.aspx"/>
<siteMapNode title="State Management" url="~/state.aspx"/>
<siteMapNode title="Post-Cache Substitution " url="~/postcache.aspx"/>
</siteMapNode>
<siteMapNode title="Server Controls" url="~/controls.aspx">
<siteMapNode title="GridView Control" url="~/gridview.aspx"/>
<siteMapNode title="Menu Control" url="~/menu.aspx"/>
<siteMapNode title="Wizard Control" url="~/wizard.aspx"/>
<siteMapNode title="Login Control" url="~/login.aspx"/>
</siteMapNode>
</siteMapNode>
</siteMap>
<%@ Page Language="C#" Theme="Default" %>
<html>
<head runat="server"/>
<body>
<form runat="server">
<h2>DataBinding TreeView to SiteMapDataSource</h2>
<asp:SiteMapDataSource id="SiteMapSource" runat="server"/>
<asp:TreeView id="MyTreeView"
SkinId="BulletedList3"
DataSourceId="SiteMapSource"
runat="server"
>
<LevelStyles>
<asp:TreeNodeStyle Font-Bold Font-Size="10pt" ChildNodesPadding="5" />
<asp:TreeNodeStyle Font-Underline ChildNodesPadding="5" />
</LevelStyles>
<Databindings>
<asp:TreeNodeBinding TextField="Title" NavigateUrlField="Url" />
</Databindings>
</asp:TreeView>
</form>
</body>
</html>