转自:

http://www.cnblogs.com/tsoukw/archive/2010/09/27/1836485.html

 

喜欢金庸的武侠,对他那几部小说也是乐此不疲


拿独孤求败来说,他的剑,从无名利剑,玄铁重剑,到木剑乃至最后的无剑,不知道破世间多少玄机


软件设计与用剑也颇有几分相似之处



下面就拿大家耳熟能详的权限设计为例,聊聊我对权限设计理解的三个层次吧


第一层:手中有剑,心中无剑


年少时,凭着手上的这把无名利剑,锋芒毕露,以为只要有这件利器在手,这天下便唯我独尊。



"XX,你那个薪资查询系统,怎么谁都可以进去查啊?"


"啊?..."


"只有总经理和他的助理可以查!"


"哦..."


if(Session["UserID"]==null)


        throw new PermissionException("必须登录");


string userID = Session["UserID"].ToString();


if(!(userID=="88.00" || userID=="888.00"))   //总经理的用户ID 88,总经理的助理用户ID:888


         throw new PermissionException("您没有访问此页的权限");


权限这玩意刚开始其实非常简单,谁又能不会?



然后听说大部分页面都要管控,很快:


public class PermissionHelper


{


     public static bool CheckPermission(string pageID);


}


用一个方法统一封装那部分代码。


再建一个table,记入哪些页面可以给哪些user访问


pageID  userID


-------   --------


薪资查询 88.00


薪资查询 888.00


薪资输入 99.00     --人事主管


----------------------------------


这样只要在PageLoad中CheckPermission一下就行



接着,又可以由要进行权限管控的页面统一继承一个类,然后只在这基类中call一次CheckPermission就行


public class PermissionPage:System.Web.UI.Page{


     override PageLoad(){


          PermissionHelper.CheckPermission(pageID);


      }


      protected abstract string pageID{get;}


}


权限页面继承此类,override pageID,就能完成当前页的权限管控



当然在asp.net中还有其它的一些方法,如利用asp.net的HttpModule,可以在访问页面之前就完成权限管控,这种方法耦合性更低,灵活性更高,不过可能就要多费一些功夫记录一下Request的Url和PageID对应才可


url    pageID


------------------


SalaryQuery.aspx   薪资查询


SalaryEdit.aspx   薪资输入


------------------------------------



昨夜西风凋碧树。独上高楼,望尽天涯路


执着,加上勤奋,终于利剑出鞘,手到擒来



当然,世间万物,各有不同


有通过控制WebForm的各种控件的状态(如Button的Enable,Visible)来控制权限的


也有在MVC中,通过在Controller中设定Attribute完成


还有控制菜单


控制数据库中table的栏位以及增/删/改/查的


可谓八仙过海,各显神通,最终都在自己的势力范围下建功立业



剑,是好剑,也能杀人


不过却不明白这把剑好在哪?


于是就今天用的还是青龙剑,明天却又发现了偃月刀也很犀利,可能也就转成刀客了


停留在此种境界,即是心中无剑,最终也不过是个剑客罢了



要做剑圣,还必须真正地研究各种不同的剑,领悟剑的本质,锻造出适合自己风格的绝世好剑--玄铁重剑


这也是第二层境界:手中有剑,心中也有剑



心中有剑后,手中的剑才能直指目标,更犀利,更直接,杀人于无形,真正雄霸天下。


要到这一层,除了要有“为伊消得人憔悴,衣带渐宽终不悔”的勇气和毅力,


还要悟



权限是什么


无非就是要让没有权限的人不能访问没有授权的物


那谁有权,谁没权


这就是权限的表示问题


不管是01也好,Table记录也罢,文件配置也行


最终都要告诉


人--物的对应,只能在有了这种对应之后,我们的权限管控才是物有所托。



当然,很多时候这两者不是直接对应的,


为了某种原因,如更易管理,更易写程式,更易提供界面等等,往往会设计一些中间过程


如人会加到群组里面,由群组来对应物


物也可以组成群组


但是不管中间多少层,最终还是人与物的对应。


最终要提供类似


HasPermission(UserID,ResourceID)(有无权限),


PermissionResource(UserID)(用户权限资源),


PermissionUser(ResourceID)(拥有资源权限的用户)等方法。



有了权限的表示,并不能阻止访问没有授权的资源,它只是一个死物,需要有地方去用它。


这就是权限的管控,它包括


1.选择管控的地点,即在哪里下手,在哪里进行管控


有通过PageLoad,有通过HttpModule,还有通过AOP在方法调用前横切管控等等


如果是资源权限,则可能在资源下拉框中按权限筛选,在提交时根据参数判断资源权限等等。


最终只要你记得,这是管控的地点


如何在系统中更简单,更方便地管控,取决于系统架构,其灵活性也让管控地点的选择是否顺利与简单



2.管控过程,分为四步


a.识别出人


cookie,Session,或者是c/s中的UserID变量,也可以是webservice的soap头经过登录后的用户ID,还可以是IP或者手机号码,最终都转为权限表示中的UserID


b.识别出物


Url,参数中的变量都行,最终要转成权限表示中的ObjectID


c.调用HasPermission(人,物)判断权限


这是由权限表示决定的方法


d.实施管控策略


对于无权限者,或转向无权登录页,或抛出异常,或Button.Visible = false最终实现权限管控



经过这个本质的识别,接下来就可以来锻造真正的玄铁重剑了


针对权限表示,设计一个比较通用的方案


也就是基本上能够通用的最简单抽象


---------------------------------


不吐不快,插播一下


在系统架构过程中,如何快速实现可供用户测试和使用的系统才是最重要的,至于一些底层服务框架,如数据访问,AOP横切,IOC,日志等,并不是越完美越好,而是要简单,要在自己全部理解的基础上使用,有了这个基础,就算碰到不能实现某些需求时,也可以很容易地通过自己修改去实现


就说日志吧,对于一些新手来说,完全没必要去用log4net,直接几行代码使用txt就完成日志记录了,要不然在程式出错时,除了要找寻程式为啥出错,还可能要去找为什么没有日志出来


还有如AOP,先想想整体架构,为什么需要AOP,AOP何时使用,如果使用spring.net,caslte,就会出现只要一个轮子,结果将整个汽车仓库都搬来了,不是不好,而是用不到,用不到再好的东西也等于零,关键是对于不熟悉的人,一旦出现问题,那维护起来也是相当麻烦


当然以上只是适用于不太熟悉的人,如果你对Castle或者log4net熟悉得就像老婆身上有哪几颗痣,在什么地方都知道,则另当别论


好了,拉回来,不跑远了


--------------------------------------


1:Object,Group的对应


2:User,Group的对应


基本上这个简单的抽象,就可以完成绝大部分权限表示问题(如果真有幸,碰到了剩下5%不能完成的权限表示时,那就再去抽象一次,最终提供权限表示上的权限方法就是)


对于权限方法,也只需写一遍,就可以用在任何权限类别上了


复制几个权限配置的片段吧


Ajax的权限


--------------------------


Role,Object


--------------------------
Admin,*


?,PCIWeb.ProgramsHelper.Programs


?,ClientTool.*


?,WebFileBrowser_CIFiles.*
?,WebFileBrowser_PQM.*
?,WebFileBrowser_ISO.*


?,MRB.BookingService.RoomBooking
?,MRB.BookingService.RoomBookingAfterNow
*,MRB.BookingService.Cancel
*,MRB.BookingService.Booking


--PQM系統
PQM_Exced,PQM.ExcedService.*
--7S,Lean,Kaizen
PQM_CI,PQM.CI



这是直接访问DB服务的权限


--------------------------


Role,Object


--------------------------
Admin,*


?,MRB/Room_Query
?,MRB/Borrow_Query2


--任何人都可以查詢每日超標回饋表
?,PQM/Exced_Query
?,PQM/Excem_Query
?,PQM/Excedd_Query
?,PQM/Excem_Query2



这是User与Role的对应


------------------------------


User,Role


------------------------------
850.00,Admin


850.00,PQM_Exced
206.00,PQM_Exced


54.00,Admin
54.00,PQM_Exced



其次,对于管控地点,不同的人的系统架构不同,可能实现也不一样。


笔者的系统架构采用RIA架构 + SOA服务,


因此在服务层使用aop的横切方式,就完成了服务权限的管控


而数据权限,也是抽象了几个UI控件,通过PermissionResource方法过滤下拉列表框和弹出Grid,而在服务调用时,在具体的程式中直接调用HasPermission方法进行管控



融入剑本质的玄铁重剑出炉,所向披靡


这把剑与当初那把无名利剑表面上似乎没有什么不同,但他的锻造过程更标准,剑招也需要内力的支撑。


举个例子来说,


通过URL管控,可能只能用于Web


而通过管控Button的Visible,虽然能管控到WinForm,但是WebService的权限怎么办


而明确了权限的本质后,针对各种不同的部分统一或单独实现,做到有的放矢,不再盲目跟从。


特别是如果自己的系统针对某一范围,更能够实现一套符合自己的通用的权限架构。



有了这一层的实现后,第三层的境界其实也是水到渠成,这便是


手中无剑,心中有剑


剑终究有形,不管它多轻,又或多通用,到处带着,始终会很累


只要心中有剑,就是手上拿着的是木剑也能杀人。



笔者所服务的部门留有很多旧系统,里面各种权限实现五花八门,加上移植兄弟公司的系统,里面也有单独的权限实现,对这一部分,原来还想全部改写,后来终于放下。


只要实现目的,管他什么手段


只是与刚开始的漫无目的不同,这时候已能十分清楚这些额外的权限是如何实现,有无漏洞等等


驾驭起来,也是顺风顺水



其实mvc的controller管控也非常不错,没有谁在这里做了漏网之鱼


其实webform的control管控也运行稳定,至今顺利在跑



存在即合理


为什么要去改变他们,为什么要花这么大心思去补救,去重构


生命中要做的事情太多太多 ,比权限管控更重要的事情也多得多,如何让用户查起来更顺手,如何让资料显示得更舒服,如何让画面更流畅。。。


资源管控住了就行,管它用什么方法,合适的就是最好的。


如一个webservice


系统总共就这么一个webservice


只允许本机访问(同一台主机,不同系统的数据交换)


那就在方法中直接写


if(HttpContext.Current.Request.UserHostAddress!="127.0.0.1")


    throw new PermissionException("抱歉,你没有访问这个web服务的权限");


这其中不也包括了


权限表示:hardcode在代码中,就是用户为127.0.0.1才有权限


管理地点:执行方法的主体代码前


识别人: HttpContext.Current.Request.UserHostAddress


识别物:就是当前管控的方法调用(在这里是隐式的)


调用权限表示方法验证:HttpContext.Current.Request.UserHostAddress!="127.0.0.1"


无权时的动作:throw new PermissionException("抱歉,你没有访问这个web服务的权限")


同样它就是一个标准的权限管控方案



信手拈来即是剑


按照权限思路,查看是否有漏洞,有则补之,无则放行


无剑一身轻



众里寻她千百度,蓦然回首,那人却在灯火阑珊处

posted on 2011-10-26 22:09  湛然常寂  阅读(451)  评论(0编辑  收藏  举报