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

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

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

在上篇解耦的故事中,我把权限设计分成了2类,一类是以系统功能为出发点管控系统的访问权限,并且将页面的权限功能模块与业务模块解耦。很多网友提出第2类的数据权限管控比较重要,所以今天就第2类权限的设计思想再详细描述一下。

 

以一个例子开始

类似在人事考勤查询中,不同人员能够查询的人事考勤信息应该是不一样的,如只可以查到自己部门的人员信息。

 

于是可能在代码中会这样写到:

Dept dept = GetUserDept(CurrentUserID);         //获取当前登录用户的部门

DataTable dt = Query(dept);                //按照这个部门查询人事考勤信息

dataGrid1.DataSource = dt;

dataGrid1.DataBind();                     //绑定DataGrid,显示在网页上

 

很快user就会把需求变更到,管理员可以查所有部门的人员信息。有的主管管多个部门,可以查询到他所管理的部门的人员信息。显然,这个时候使用当前登录用户的部门来作为查询依据已经无法满足了,于是,权限部门的概念就呼之欲出。即给用户分配部门,他有几个部门,他就能查询到几个部门的信息。代码当然很简单,如下:

List<Dept> deptList = GetUserRightDepts(CurrentUserID);  //获取当前登录用户的权限部门

dropdownlist1.DataSource = deptList;    //绑定部门下拉框(user只能在权限部门里面选择,并查询)

dropdownlist1.DataTextField = "Dept_Name";

dropdownlist1.DataValueField = "Dept_No";

dropdownlist1.DataBind();

 

可能很多系统设计师到这里也就结束了,最多封装成一个控件,然后每次使用时直接拖到aspx页面,这样做也行,不过无论如何,还是觉得别扭,为什么,因为很明显,这个aspx页面与权限耦合在一起了,其实,想象一下,这个页面和权限关系有这么大吗?完全没有,去看那些查询UI框,查询方法的实现,都不是,但是为什么就会耦合在一起呢,观念没有转过来。

 

解耦

 

很明显,整个页面如果像上面那样写,那就和GetUserRightDepts方法耦合在一起呢,事实上,这个页面最不关心的就是到底是GetUserRightDepts方法,还是其它Get***Depts方法,只要最终都是返回一个List<Dept>结果,那就OK了。

 

权限与业务不要耦合,因为两者实际上关系不大,有无权限,并不会决定这个程序的执行过程(如上述这段代码)。

 

那怎么办,很简单,首先转换观念,写这支程序实现的就是查询用户所选部门的考勤数据,没必要我写的查询程序只能查询权限部门,你也可以通过QueryString把能查询的Dept带过来。

 

而在程序中只想通过一个约定的方法或到一个固定的地方去取就行,如:

(List<Dept>)HttpContext.Current.Items["DeptList"],然后再用这个List去绑定部门下拉列表框就完成了。

现在已不需要Hardcode调用GetUserRightList方法了,那这个方法放在哪里呢asp.net中很简单,放在一个地方统一处理这类的权限问题即可(如:HttpModule)

 

实现一个HttpModule

这个HttpModule会在程序Request之前的某个事件中就注入某支程序所需要的参数,如

 

上面这支查询程序的部门列表。

 

注入方式很简单,可能通过在某个地方配置,如

<Injection Url="query/考勤Query.aspx">

   <Item Name="DeptList" Type="RightDept" />

</Injection>

 

然后在Module中统一读取这个配置,按照不同的Type去选择不同的注入方式,如

RightDept,就会调用GetUserRightDept方法,然后将结果放到HttpContext.Items["DeptList"]方法中。

一个示例:

    public class SecurityModule : IHttpModule

    {

 

        public SecurityModule()

        {

        }

 

        public void Init(HttpApplication application)

        {

            //捕获PreRequest事件

            application.PreRequestHandlerExecute += new EventHandler(application_PreRequestHandlerExecute);

        }

 

        void application_PreRequestHandlerExecute(object sender, EventArgs e)

        {

            //注入数据权限

            LoginHelper lo = new LoginHelper();

             List<string> deptList = null;

            if(lo.GetUserID()=="admin")

                 deptList = new List<string>(new string[] { "电脑", "人事部", "财务部" });

            else if(lo.GetUserID()=="zkw")

                 deptList = new List<string>(new string[] { "电脑"});

             HttpContext.Current.Items["DeptList"] = deptList;

        }

 

        public void Dispose()

        {

        }

 

    }

aspx页面示例:

    protected void Page_Load(object sender, EventArgs e)

    {

        if (HttpContext.Current.Items["DeptList"] != null)

        {

            List<string> depts = (List<string>)HttpContext.Current.Items["DeptList"];

            Response.Write("你的权限部门是:<br>");

            foreach (string dept in depts)

            {

                Response.Write(dept + "<br>");

            }

        }

    }

 

于是数据权限就统一在程序之外,并且可以灵活配置了。

 

一直以权限设计作为解耦的故事主题,但是最重要的还是一个设计思想,不单是权限的问题,像我们经常在aspx中的Page_Load中写到

string kind = Request.QueryString["Kind"]

这样的代码,它也是耦合,将kind的获取方式强耦合到QueryString上。如果需要通过另一种方式传递kind到这个aspx页面时,一定又要修改代码。

 

可以在写程序时多问问自己,是否kind一定就会通过QueryString获得,如果不是,那要怎么改?

 

这些观念其实每个写程序久一点的人或多或少都有,只是每个人的解决方案不同。如果您了解IOC,再对比的想一下吧。

 

BTW:接触过的很多朋友,在一个新东西出来时,盲目跟风,会按照那个东东做例子,但是一旦到正式的Project时,又按照自己的方式来。

他不明白那个东东替它解决什么问题,因为他根本没有意识到还有那个问题的存在。

结果发现套用框架或使用这个平台时很复杂,还不如按照自己的方式去写,至少比较容易理解。

 

WFAOPIOC等,知其然,还要知其所以然。

 

PS:后面这一段是在听到一个同事要在一个签核模块中用WF状态流来设计时有感而发。

 

另外,应博客园的出版要求,对《web应用程序安全的思考》系列进行了修改,并且改动较大。和这篇文章一起,相信可以给设计师对web系统的权限管控设计给出一个全面的方案

 

 

posted on 2008-11-17 10:22  Kevin Zou  阅读(3126)  评论(4编辑  收藏  举报