十年磨一劍--從程序員到架構師

一个.net程序员,一个企业应用的开发者,喜欢系统架构,数据库,领域驱动,面向对象,表现层技术。关注重用的理论和实践。设计原则:简单,快速,适应变化能力强,表现层灵活多变...

博客园 首页 新随笔 联系 订阅 管理

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

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

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

 

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

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

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

 

"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 2010-09-27 09:56  Kevin Zou  阅读(6376)  评论(23编辑  收藏  举报