webabcd - 专注于asp.net

ASP.NET
从现在开始 一切都不晚
posts - 149, comments - 4168, trackbacks - 344, articles - 0
  博客园 :: 首页 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理
原文地址:http://aspnet.4guysfromrolla.com/articles/053007-1.aspx
[原文源码下载]


[翻译]开发自己的web站点管理工具(Website Administration Tool)(2)


原文发布日期:2007.05.30
作者:Dan Clem
翻译:webabcd


介绍
为了方便管理用户、角色和权限设置,ASP.NET 2.0内置了Web Site Administration Tool (WSAT),你可以在Visual Studio 2005的“网站”菜单下选择“ASP.NET 配置”来启动WSAT。 但是,WSAT只允许你管理本地的web站点。 当web站点部署在远程web主机上的时候,WSAT就会受到一些限制。 所以,我决定从零开始开发一个自己的WSAT程序。本系列文章的第一部分从整体上讲述了我的自定义WSAT程序的实现,并且讲解了其中关于用户管理的部分。

本文是该系列文章的第二部分,也是最后一部分,在这里我会详细讲解上一篇文章中没有说明的一些要点: 角色管理和访问规则管理。 你可以在本文的结尾处下载我写的WSAT程序的全部代码。 关于如何在一个新建或已有的web程序中使用我的这个WSAT程序,你可以参考本系列文章的第一部分(译者注:中文在这里)中的“使用我的自定义web站点管理工具(WSAT)”。 继续往下看,你会学到更多的在我的自定义WSAT程序中关于角色管理和访问规则管理的相关知识。


你最好已经看过本系列文章的第一部分
在看本文之前,你最好已经阅读过本系列文章的第一部分(译者注:中文在这里)。在第一部分中,我从整体上讲述了我的自定义WSAT程序的实现,并且一步一步地说明了如何在一个新建或已有的web程序中使用我的这个WSAT程序,另外,如何实现管理用户的功能我也做了充分的讲解。 你可以在本文结尾处或本系列文章的第一部分的结尾处下载这个自定义的WSAT程序的全部代码。

角色管理
我的自定义WSAT程序的角色管理部分完成了如下3个任务:
    ·显示系统内的所有角色
    ·允许删除一个已存在的角色
    ·允许新建角色

我把实现这3个任务的功能恰当地放到了一个web页面内,其截屏如下:


我使用了一个GridView来显示所有角色。 Roles类的GetAllRoles方法返回的是系统内的角色名数组(字符串数组)。 除了列出所有角色外,我还想显示出每一个角色所关联的用户的数量。 所以,直接通过GetAllRoles方法返回一个字符串数组绑定给GridView满足不了我的需求,于是我决定构造一个自己的DataTable,并使其作为GridView的数据源。 我把这段代码写到了Page_PreRender方法内。
// 新建一个DataTable,并定义它的列
DataTable RoleList = new DataTable();
RoleList.Columns.Add(
"Role Name");
RoleList.Columns.Add(
"User Count");

// 获取角色列表和每一个角色下的用户数
string[] allRoles = Roles.GetAllRoles();

foreach (string roleName in allRoles)
{
   
int numberOfUsersInRole = Roles.GetUsersInRole(roleName).Length;
   
string[] roleRow = { roleName, numberOfUsersInRole.ToString() };
   RoleList.Rows.Add(roleRow);
}


// 绑定DataTable给GridView
UserRoles.DataSource = RoleList;
UserRoles.DataBind();  

向系统内添加一个新的角色,只需要下面这样的一行代码:
Roles.CreateRole(roleName);

同样,删除一个角色也只需要一行代码:
Roles.DeleteRole(roleName);

需要注意的是,如果一个角色内有一个或更多的用户,那么这时你要删除这个角色的话,默认的Provider就会抛出一个异常。 所以,在删除一个角色的时候要先移出角色内的所有成员。你可以调用Roles类的RemoveUsersFromRole方法来做这项工作。 该方法需要两个参数: 角色内需要移除的成员的用户名数组和角色名称。 你可以使用GetUsersInRole方法来得到指定角色下的所有用户(返回的是一个包含用户名称的字符串数组)。


访问规则管理
在“访问规则管理”页中,使用了一个TreeView控件来显示web程序内的目录(文件夹)结构列表。 树型结构的显示是通过Page_Load事件调用的PopulateTree()方法内的代码实现的。 (我从Scott Mitchell的文章Using the TreeView Control and a DataList to Create an Online Image Gallery中几乎是一个字一个字地复制了PopulateTree方法和AddNodeAndDescendents方法)

要使用“访问规则管理”这个功能的话,你需要以隶属于管理员角色的用户登录。 在详细讲解之前,我们先来看看.net自带的WSAT程序中总结的一句话: “规则按顺序应用。应用符合条件的第一个规则,每个规则中的权限重写其后所有规则中的权限。”

访问规则的设置保存在每一个文件夹中的Web.config文件内。 你可以手写这些Web.config文件,也可以使用WebConfigurationManager类对访问规则进行管理。 注意一下你的ASP.NET程序是在Windows的哪个用户之下运行的,一般来说应该是 NT Authority\Network Service用户,你必须要保证这个用户有读写Web.config文件的权限。

在“访问规则管理”页中,管理员角色的用户可以通过单击左侧的目录树视图,来管理指定文件夹的访问规则。 你可以对访问规则进行添加、移动或删除的操作。 子文件夹的访问规则会继承其父文件夹,但是不可以在子文件夹中更改在父文件夹中制定的访问规则。 你可以通过指定用户或角色的Deny和Allow行为来创建新的访问规则。 我的这个WSAT程序可以设置你所选择的用户或角色的权限, 也可以指定所有用户(*)或匿名用户(?)的权限。


添加规则分两步走: 首先新建一个规则,然后将其添加到web配置中。
// 新建规则
AuthorizationRule newRule;
if (ActionAllow.Checked)    newRule = new AuthorizationRule(AuthorizationRuleAction.Allow);
else
   newRule 
= new AuthorizationRule(AuthorizationRuleAction.Deny);


// 添加规则
if (ApplyRole.Checked && UserRoles.SelectedIndex > 0)
{
   newRule.Roles.Add(UserRoles.Text);
   AddRule(newRule);
}

else if (ApplyUser.Checked && UserList.SelectedIndex > 0)
{
   newRule.Users.Add(UserList.Text);
   AddRule(newRule);
}

else if (ApplyAllUsers.Checked)
{
   newRule.Users.Add(
"*");
   AddRule(newRule);
}

else if (ApplyAnonUser.Checked)
{
   newRule.Users.Add(
"?");
   AddRule(newRule);
}

AddRule方法用于访问指定文件夹的配置文件,并添加我们的权限规则。
private void AddRule(AuthorizationRule newRule)
{
   
string virtualFolderPath = FolderTree.SelectedValue;

   Configuration config 
= WebConfigurationManager.OpenWebConfiguration(virtualFolderPath);
   SystemWebSectionGroup systemWeb 
= (SystemWebSectionGroup)config.GetSectionGroup("system.web");
   AuthorizationSection section 
= (AuthorizationSection)systemWeb.Sections["authorization"];

   section.Rules.Add(newRule);

   
try
   
{
      config.Save();
      RuleCreationError.Visible 
= false;
   }

   
catch (Exception ex)
   
{
      RuleCreationError.Visible 
= true;
      RuleCreationError.Text 
= "<div class=\"alert\">An error occurred and the rule was not added.<i>" + ex.Message + "</i></div>";
   }

}
 

向上或向下移动规则需要我们自己来手写更多的代码,因为.net没有提供内置的方法来实现这个功能。 我用的方法是通过一个数组来排序各个规则,从而完成这项工作。 我认为这个方法还是比较简单的,所以就不详细说明了,其主要就是先从配置文件中读出所有规则并放到一个ArrayList对象中,同时再删除配置文件中的所有规则,然后根据用户的需要对这个数组进行排序,最后再将排序好的规则重新写人配置文件中。


“访问规则摘要”页的实现方法
让我们开发我们自已的“访问规则摘要”页吧,在.net内置的WSAT程序中是没有这个功能的。 隶属于管理员角色的用户可以在这个页中查看指定的角色或用户在各个目录中的权限。

在这个页中,角色和用户的选择是通过DropDownList控件实现的。 当你选择了一个角色或用户之后,TreeView控件就会列出web程序内的所有文件夹。 它与“访问规则管理”页中的TreeView控件比较相似,所不同的就是这个TreeView控件用红色和绿色标记来表示你所选择的角色或用户在某目录中的权限。 当然就像你认为的那样,绿色代表你所选择的角色或用户有访问的权限,红色则正好相反。 除了红色和绿色标记之外,TreeView控件上的文本还会显示出为什么你所选择的角色或用户会有这样的权限。 通过下面的截屏你就可以发现,被选中的用户为“Franklin Forester”,因为他属于“Marketing”角色,所以有对marketing文件夹的访问权限。



让我们的自定义WSAT程序安全地运行
现在我们就可以把这个自定义WSAT程序移植到我们的新建或已有的web站点里,为了保证程序的安全,我们希望只有管理员才能访问它。 为了实现这个功能,需要增加两个访问规则。 在我的例子中,我只允许管理员角色有对admin文件夹的访问权限,然后拒绝其他所有用户,如下图所。 注意,允许管理员角色访问的规则必须在拒绝所有用户访问的规则的上面,否则将不会有任何人有权限访问这个文件夹。(如果你不小心使得管理员角色无法访问admin文件夹的话, 那么你就需要手动修改Web.config文件,以使管理员角色可以访问你的自定义WSAT程序的admin文件夹。) 



结论
虽然可以通过ASP.NET 2.0内置的web站点管理工具(WSAT)来管理用户、角色和权限,但是该工具只能在本地使用,而且有一些功能上的不足。 所以本文和本系列文章的第一部分(译者注:中文在这里)探讨了如何实现一个自定义的WSAT程序。 这个自定义WSAT程序可以移植到你的新建或已有的 ASP.NET web程序中,并且可以部署在远程服务器。 此外,为了改进原来的WSAT程序,它还附加了一些新的特性。


祝编程愉快!

Feedback

#1楼    回复  引用    

2007-06-17 23:57 by jack lee [未注册用户]
受益了,支持一下

#2楼    回复  引用    

2007-06-18 00:12 by 六天七夜 [未注册用户]
你最好已经看过本系列文章的第一部分
http://www.10089.org.cn/

#3楼 [楼主]   回复  引用  查看    

2007-06-18 08:06 by webabcd      
@jack lee
:)

@六天七夜
广告?

#4楼    回复  引用  查看    

2007-06-18 09:27 by 领悟      
如果有源码就不错了

#5楼 [楼主]   回复  引用  查看    

2007-06-18 09:48 by webabcd      
@领悟
文章开头部分有下载源码的地址

#6楼    回复  引用  查看    

2007-06-18 09:58 by 领悟      
哦,刚没有看到,谢谢

#7楼 [楼主]   回复  引用  查看    

2007-06-18 11:30 by webabcd      
@领悟
:)
不谢

#8楼    回复  引用    

2007-06-18 17:26 by xinlan [未注册用户]
学习

#9楼 [楼主]   回复  引用  查看    

2007-06-18 20:30 by webabcd      
@xinlan
:)
取其精华,弃其糟粕

#10楼    回复  引用  查看    

2007-06-18 20:43 by 高海东      
很好啊 这个很好 可以开发个产品

#11楼 [楼主]   回复  引用  查看    

2007-06-18 20:56 by webabcd      
@高海东
嗯,确实,感觉比.net自带的WSAT程序强很多

#12楼    回复  引用  查看    

2007-06-19 09:16 by 里奥特      
支持了.

#13楼 [楼主]   回复  引用  查看    

2007-06-19 10:46 by webabcd      
@里奥特
:)

#14楼    回复  引用    

2007-07-03 18:25 by 王鹏 [未注册用户]
dsygdfh

#15楼 [楼主]   回复  引用  查看    

2007-07-03 18:55 by webabcd      
@王鹏
。。。破解不了

#16楼    回复  引用    

2007-09-06 17:45 by raorf [未注册用户]
good!!!

#17楼 [楼主]   回复  引用  查看    

2007-09-06 19:32 by webabcd      
@raorf
:)
确实是很实用的东西

#18楼    回复  引用    

2007-09-16 01:02 by cyh [未注册用户]
为什么我想到的东西一般都已经有牛人已经实现了呢?受教了

#19楼 [楼主]   回复  引用  查看    

2007-09-17 08:07 by webabcd      
@cyh
:)
那就可以有更多的时间研究别的东西了

#20楼    回复  引用  查看    

2007-11-07 17:50 by 一刀      
Net2.0中的Login控件,登陸後通過Membership,Roles等來取相關用戶及角色(Role)信息,但不怎其是怎與Web.Config中的配制中的角色關聯的,基內部機制是什麼???多謝

#21楼 [楼主]   回复  引用  查看    

2007-11-07 18:32 by webabcd      
@一刀
那就是Provider
Membership、Roles这些有针对sqlserver的provider的源代码
兄弟可以看一看

其实知道Provider模式是怎么回事就会很清楚了

#22楼    回复  引用  查看    

2007-11-07 23:16 by 一刀      
想不到楼主会回复得那么快,多谢,
我还是不明,可能没我没说明我的意思,
我的问题是:就像在你Dome 中一个文件夹的WebConfig中定义:
<system.web>
<authorization>
<allow roles="IT" />
<deny users="*" />
</authorization>
</system.web>
假设我在数据库在某一用户没IT角色,则不允许访问的,会重定向登陆页,
我就是不知怎通过WebConfig制就可达到目的了,什么代码都不用写,是不是在Privider中已写好:)




#23楼 [楼主]   回复  引用  查看    

2007-11-08 08:12 by webabcd      
@一刀
Forms验证
根据配置的规则允许或拒绝对url资源的访问

#24楼    回复  引用  查看    

2007-11-08 11:15 by 一刀      
查了下資料,終於知了,通常用的FormsAuthentication.RedirectFromLoginPage 方法,是系統依用戶產生的Cookie,其中沒有Roles的值,所以此方法是基於用戶的,不是基於角色,要
基於角色驗証,通過
份验证票FormsAuthenticationTicket,在Login時就生FormsAuthenticationTicket實例(內有相關的角色),再寫入Cookie中,
在每次頁面請求時,Application_AuthenticateRequest事件中,重新構造HttpContext的User如下:
protected void Application_AuthorizeRequest(object sender, System.EventArgs e)
{
HttpApplication App = (HttpApplication) sender;
HttpContext Ctx = App.Context ; //获取本次Http请求相关的HttpContext对象
if (Ctx.Request.IsAuthenticated == true) //验证过的用户才进行role的处理
{
FormsIdentity Id = (FormsIdentity)Ctx.User.Identity ;
FormsAuthenticationTicket Ticket = Id.Ticket ; //取得身份验证票
string[] Roles = Ticket.UserData.Split (',') ; //将身份验证票中的role数据转成字符串数组
Ctx.User = new GenericPrincipal (Id, Roles) ; //将原有的Identity加上角色信息新建一个GenericPrincipal表示当前用户,这样当前用户就拥有了role信息
}
}

:)多謝樓主的熱心講解

#25楼 [楼主]   回复  引用  查看    

2007-11-08 12:03 by webabcd      
@一刀
兄弟很强
这东西不怕不会,知道怎么能找到答案就ok

#26楼    回复  引用    

2008-07-18 09:10 by 树的回忆 [未注册用户]
谢谢楼主了!
解决了我的一个大难题!

#27楼 [楼主]   回复  引用  查看    

2008-07-18 12:22 by webabcd      
@树的回忆
:)
不谢

翻译的,呵呵

标题  
姓名  
主页
Email (博主才能看到) 
验证码 *  看不清,换一张 [登录][注册]
内容(请不要发表任何与政治相关的内容)  
  登录  使用高级评论  新用户注册  返回页首  恢复上次提交      
该文被作者在 2007-06-17 19:28 编辑过


相关链接: