zoukankan      html  css  js  c++  java
  • 软件项目最佳实践: 又谈权限管理

    一个架构的好坏,判断标准是:是不是容易维护.
    代码量少,这是最重要的一个指标.

    权限管理也很重要,也是大家最感兴趣的一点,今天工作中又聊到这个话题,又有新意,将来再讨论。
    统一的权限架构有什么好处?统一的验证逻辑,统一的配置,方便维护。       

    不要迷失我们的终极目标:什么人对哪些数据(在什么时候)能做哪些事。

    映射到角色/功能模型的话:
    A. 一个人属于多个角色
    B. 一个角色能操作哪些功能项
    C. 一个角色能读写哪些数据
     

    只要能实现这样的控制逻辑,都是好用的权限管理架构。

    我的做法是:不能操作就引发无此权限异常,这样方便编码,更有效地保证安全。

    A和B都好理解,实现起来也容易。
    针对C,我的做法是:对每种数据的操作都定义一个角色。
    也就是说角色有N多,除了预定义的角色外,还能不停地生成。
    如,部门经理这个角色是系统预定义的,但还有”一部经理“,”二部经理“,”三部经理“这样的角色,我们称作“岗位”。
    基于这个理论,我们还是套用了角色/功能的权限管理模型。

    如何来实现岗位呢?听起来有点像实例化的角色对象,实现起来也简单:引入“鉴权参数”的概念。
    “部门经理”是预定义的角色 DeptManageRole
    此角色有一个属性叫:DeptCode。值 = “一部“,这就是鉴权参数

    一部经理就是 new DeptManageRole(){DeptCode="一部"},也是一个角色,在编写控制逻辑,鉴定操作权限时,

    除了判断是什么角色外,还要判断部门参数是不是等于一部。



    现假设,有这样的一个业务场景:
    一部经理可以在付款申请单未审批的情况下付款。
    写代码,大家都会:
    点击“付款”按钮时,若请款单的属性=未通过,当前用户有部门经理角色,付款单的部门 = 部门经理.部门参数 就继续付款,否则提示“无此权限”。


    我们在Asp.Net中通常把用户名序列化到Cookie中,我推荐把所属角色也一并保存到Cookie中,
    所以"一部经理"这个角色应可以序列化和反序列化,以便保存到Cookie。
    当然保存到数据库中也没问题,需要读写数据库而已,会有性能损失。

    //登录后,我们给当前用户赋于“一部经理“的角色:
    var role = new DeptManageRole(){DeptCode=CurrentUser.DeptCode};
    CurrentUser.AddRole(role);

    //检索数据时,当前用户只检索一部数据
    getPayBills()
    {
          payBillDbSet.where(payBill=>payBill.DeptCode = CurrentUser.AsRole<DeptManageRole>().DeptCode).select();
    }



    //付款时
    Pay(payBill)
    {
          if CurrentUser.CanNot("未审批付款“) then
               throw new exception('付款单未审批,不能付款')
          end if

         if payBill.Status = "未通过"  and payBill.DeptCode = CurrentUser.AsRole<DeptManageRole>().DeptCode then
              pay....
          else
              throw new exception('付款单未审批,不能付款')
         end if
    }



    至此,我们的权限管理已经实现了,但——
    客户老板打电话过来说:最近有几笔付款单金额很小,不必我批准,让部门经理可以付款,快教我怎么设置,给他分配权限。

    这可怎么办?合理需求啊。
    1. 权限要可配置。否则我们要改代码,要编译,要重新发布。
    2. 要可视化配置,否则客户老板不知道怎么设置,看不懂XML,更不会打开数据库表去修改字段。
    3 . 技术上,我们需要有类似这样的配置项:
          付款: DataObject.Status = "未通过" and DataObject.Amount < 100.00 and  IsRole("部门经理")  and  DataObject.DeptCode =  <部门经理>.DeptCode
        当按钮点击时:
            if  Can(thePayBill,"付款") then  //动态计算此表达式,返回true/false,实参是thePayBill,形参是 DataObject
                 pay...
             else
                 throw new Exception(“不能付款”)
             end if


    我们再回到角色的配置上来:
    角色可以用Xml或Table来定义。

    甚至可以是一个二维表:

                             一部                          二部                      三部
    部门经理             MA1                           MA2                     MA3                       
    部门副经理          MB1                           MB2                     MB3          
    助理                   H1                             H2                       H3


    瞧,多直观的角色,还可以更复杂:周一到周五,轮着当经理都行。

    岗位 = 角色 + 部门 + 周几。

    一个角色附加一些鉴权参数,就可以具体化此角色,成为一个新岗位,而岗位也抽象为一种角色。

    事实上,这里最不容易实现的是第2点:如何可视化配置,依据PayBill的属性来筛选数据操作权限。

    假设PayBill表没有Amount字段怎么办?没有好办法,兴许硬编码是更好的方式。
    当然,为了实现此需求,套用我们的权限功能,在PayBill表上增加一个字段就解决问题了,
    可是,表上加字段,类上加属性,似乎有背于我们统一基础权限管理愿望,怎么办?
    既然配置上统一不起来,我们退一步,至少在代码级别上可以统一使用这样的权限架构。

    实际上,试图用配置完全实现权限控制真是很难实现的:

    假设用户没权限点击某按钮时,这个功能按钮是显示Visible呢,还是不显示呢?是可用Enable呢,还是不可用呢?

    是异常中止呢?还是返回false呢?还是提示重登录呢?还是显示帮助信息呢?
    例如,QQ就会经常提示:只有会员才可点击,请立即付钱,成为钻石会员。
    有统一的地方设置当然更好,我觉得硬编码也是个不错的选择。
    只要代码风格好,逻辑清楚,为什么不一起来维护优美的诗句呢?


    这里还有一个变态的角色:
    每次当部门经理出差后,A君就要代理部门副经理的角色,审批一些单据。
    怎样才能让A君不必重新登录系统,一旦部门经理点击了“我已出差”后,A君就可以做代审批的操作?

    套用我们的权限管理架构,可以实现:
    审批时:
    代经理.领导已出差 =  return 判断领导是否在公司();
    if CurrentUser.AsRole(代经理).部门 = DataObject.部门 and CurrentUser.AsRole(代经理).领导已出差 = true then
        可以批
    else
        不可以批
    end if


    很快,这里又产生了一个现实的问题:

    通常我们的角色叠加时,对功能项的操作权限也越大,

    比如当前操作人员既是一部业务经理,又兼职二部财务经理时,他应该可以点击更多按钮,看到更多数据。

    实际编码中发现,如果具体化的岗位来限制数据检索权限时,where子句 and 条件就会叠加,

    最终结果是:既看不到一部数据,也看不到二部数据。

    如果要叠加数据操作权限,就要用 or 来连接 where 条件子句,这增加了编码复杂度。

    推荐做法是(感谢宁波罗经理的建议):

    这种情形,我们简化角色模型,用户在登录时,要么选择以一部经理身份登录,

    要么选择以二部财务经理身份登录,不要同时混用这两种岗位。

    如此,编程模型就简单多了,再也不必纠结在数据检索权限的控制上了。

    再考虑一个复杂的权限表:

    这样的权限表复杂吧?如果用鉴权参数来设定角色就轻而易举了.

    只要定义一种角色:负责人,把部门+产品线+市场作为鉴权参数即可。

    那么可视化的权限设置表该怎样设计?预置的权限配置方式肯定不直观,我们还须为此权限表完整地开发一个设置功能,

    管理员直接双击单元格,录入人名就可以完成设置。如果是开发人员来设置这个权限表就打开XML文件,可以手动改写。

    看来已经没有什么需求能难倒我们了,能想到的都可以套用此权限管理架构,

    但我们不明确复杂的业务将来会演变成什么需求,永远没有办法论证未来会发生的事情。

    在这里,我们没有引及组(Group)的概念,组就是把“A君和B君”设定为一个组,这样赋角色时,操作可以简便一点。

    另外,我们也没有引入角色继承的概念,所有角色都是扁平的,避免权限编程模型过度复杂。

    高手会说:这......是个度的问题。我说:有一个是最佳实践.

  • 相关阅读:
    CPDA之时间序列
    CSS clear
    27 款经典的CSS 框架
    IT人必读:请不要做浮躁的IT人
    23个Javascript弹出窗口特效
    三种东西永远不要放到数据库里
    jquery 插件 thickbox窗口 第一个控件获得焦点(解决第二次弹出窗口,文本不能输入数据)
    enum总结
    vs版本的选择
    iPhone iPad 各种控件默认高度
  • 原文地址:https://www.cnblogs.com/heguo/p/2598405.html
Copyright © 2011-2022 走看看