将权限引入系统的探索

    很久以前就尝试过 使用AOP进行权限控制的管理,那时候只是试试而已,并没有在项目中应用过,最近跟的一个B/S项目中要求我们把权限管理加进去,于是我考虑以前的AOP方法是否真的可行。思考许久后的结果是,可行,但是太麻烦。
    主要原因在于需要在UI层<=>逻辑层<=>数据层之外增加额外的一层来使得AOP拦截可以以恰当的粒度、恰当的时机切入。如果不增加额外的这一层而使用AOP就可能会得到这样的结果--比如,在业务逻辑层或数据层使用AOP切入,就有可能出现这样的情形,用户花了老大的劲将要录入的信息敲入了,最后当点击“提交”时,系统却提示说“你没有权限进行添加操作!”。如果你是这个用户,你一定会郁闷得吐血!
    所以,如果系统不允许用户添加,则根本就不应该让添加的Page或Form出现在用户眼前,然而,这在逻辑层或数据层是无法获取这个信息的,只有UI层知道这是发生在什么时刻。但是,在UI层使用AOP截获,你看见过么?我没有,我相信那不是一件容易做的工作。

    于是,我开始寻找AOP之外的方法,终于,我找到了一个自己还比较满意的解决方案。下面我们以B/S系统为例,来详细描述这个方案,如果是C/S系统,可以类推之。
    为了构建一个在不同B/S系统中可以复用的解决方案,我们需要规范化(定义)一些基础设施或元素。首先,我们需要定义权限的类别,比如最常见的有浏览、添加、修改、删除。你也许已经想到使用一个枚举就可以了。但是,不能使用枚举,因为枚举是sealed,不能被继承,这就意味着权限类别将不能被自定义扩展,所以,我没有使用enum,而是使用class。
    ///   <summary>
    
///  应用可以扩展PermissionType,以增加其它权限类别,比如审核等
    
///  自定义类别取值应大于 5 
    
///   </summary
    public class PermissionType
    {
        
public   const   int  Browse  =   1  ; 
        
public   const   int  Add     =   2  ;
        
public   const   int  Update  =   3  ;
        
public   const   int  Delete  =   4  ;
    }
     这样,如果具体应用中有更多的权限类别,只要从PermissionType继承即可。比如,应用中需要添加“审核”、“消审”的权限,则可以
    public   class  MGPPermissionType : PermissionType
    {
        
public   const   int  Audit  =   10  ;        // 审核
         public   const   int  CancelAudit  =   11  ;  // 消审
    }
 
    一个用户所拥有的权限可以使用IPermission接库进行规范:
    ///   <summary>
    
///  应用可以扩展IPermission,以增加其它权限类别,比如审核等
    
///   </summary>
     public   interface  IPermission
    {
        
bool  CanBrowse{ get  ;} 
        
bool  CanAdd{ get  ;}
        
bool  CanUpdate{ get  ;} 
        
bool  CanDelete{ get  ;}
    }
    如果应用需要增加别的权限,也可以在实现这个接口的类中进行扩展,比如
GeneralPermission
    #region GeneralPermission
    
public class GeneralPermission :IPermission
    {
        
public bool HasNonePermit
        {
            
get
            {
                
return ! (CanBrowse || CanAdd || CanUpdate || CanDelete || CanAudit || CanCancelAudit) ;
            }
        }

        
#region IPermission 成员
        
#region CanBrowse
        
private bool canBrowse = false ; 
        
public bool CanBrowse
        {
            
get
            {
                
return this.canBrowse ;
            }
            
set
            {
                
this.canBrowse = value ;
            }
        }
        
#endregion
        
        
#region CanAdd
        
private bool canAdd = false ; 
        
public bool CanAdd
        {
            
get
            {
                
return this.canAdd ;
            }
            
set
            {
                
this.canAdd = value ;
            }
        }
        
#endregion
        
        
#region CanUpdate
        
private bool canUpdate = false ; 
        
public bool CanUpdate
        {
            
get
            {
                
return this.canUpdate ;
            }
            
set
            {
                
this.canUpdate = value ;
            }
        }
        
#endregion
        
        
#region CanDelete
        
private bool canDelete = false ; 
        
public bool CanDelete
        {
            
get
            {
                
return this.canDelete ;
            }
            
set
            {
                
this.canDelete = value ;
            }
        }
        
#endregion
        
        
#region CanAudit
        
private bool canAudit = false ; 
        
public bool CanAudit
        {
            
get
            {
                
return this.canAudit ;
            }
            
set
            {
                
this.canAudit = value ;
            }
        }
        
#endregion
        
        
#region CanCancelAudit
        
private bool canCancelAudit = false ; 
        
public bool CanCancelAudit
        {
            
get
            {
                
return this.canCancelAudit ;
            }
            
set
            {
                
this.canCancelAudit = value ;
            }
        }
        
#endregion
        


        
#endregion
    }
    GeneralPermission扩展了IPermission接口,添加了“审核”、“消审”的权限。

    在B/S系统中,如果用户要求做一件没有权限的事情时,通常会转到一个给出提示信息“没有相应权限”的page。但是,当用户没有权限时,到底该怎么做,我们把这个权利留给具体的应用,这里只是用接口来规范它:
    public   interface  IPermissionHelper
    {
        
void  OnHaveNoPermission( int  pType ,HttpResponse response) ;
        
bool  VerifyCustomPermission(IPermission per , int  pType) ;  // 验证自定义的权限类型
    }
    OnHaveNoPermission方法就是应用在用户没有权限操作时定义要做的事情。通过上面的介绍,VerifyCustomPermission方法的作用就一幕了然了。比如下面这个IPermissionHelper实现:
PermissionHelper
    public class PermissionHelper :IPermissionHelper
    {
        
#region IPermissionHelper 成员

        
public bool VerifyCustomPermission(IPermission per, int pType)
        {
            GeneralPermission gPermission 
= (GeneralPermission)per ;
            
            
bool b1 = (pType == MGPPermissionType.Audit) && (! gPermission.CanAudit) ;
            
bool b2 = (pType == MGPPermissionType.CancelAudit) && (! gPermission.CanCancelAudit) ;

            
if(b1 || b2)
            {
                
return false ;
            }

            
return true ;
        }

        
public void OnHaveNoPermission(int pType ,HttpResponse response)
        {
            response.Redirect(
"/MGPMIS/ErrorInfoForm.aspx") ;
        }

        
#endregion
    }

    好了,一切准备就绪,主角可以出场了,PermissionController--所有与权限相关的操作都有它来转发,如果权限满足,则调用目标操作或转到目标页面;如果条件不满足,就调用IPermissionHelper.OnHaveNoPermission方法,一切清晰明了。
PermissionController
    /// <summary>
    
/// 用于将B/S中的权限管理集中起来。
    
/// zhuweisky 2006.04.08
    
/// </summary>
    public class PermissionController
    {
        
private IPermissionHelper permissionHelper ;        

        
public PermissionController(IPermissionHelper helper)
        {
            
this.permissionHelper = helper ;        
        }

        
#region Excute
        
/// <summary>
        
/// 如果权限满足,则转到目标url
        
/// </summary>    
        public void Excute(IPermission per ,int pType ,HttpResponse response ,string destUrl) 
        {
            
bool b1 = (pType == PermissionType.Browse) && (! per.CanBrowse) ;
            
bool b2 = (pType == PermissionType.Add)    && (! per.CanAdd) ;
            
bool b3 = (pType == PermissionType.Update) && (! per.CanUpdate) ;
            
bool b4 = (pType == PermissionType.Delete) && (! per.CanDelete) ;

            
if(b1 || b2 || b3 || b4)
            {
                
this.permissionHelper.OnHaveNoPermission(pType ,response) ;
                
return ;
            }

            response.Redirect(destUrl) ;
        }

        
/// <summary>
        
/// 如果权限满足,则执行目标方法
        
/// </summary>        
        public void Excute(IPermission per ,int pType ,HttpResponse response ,Delegate destMethod ,object[] args) 
        {
            
bool b1 = (pType == PermissionType.Browse) && (! per.CanBrowse) ;
            
bool b2 = (pType == PermissionType.Add)    && (! per.CanAdd) ;
            
bool b3 = (pType == PermissionType.Update) && (! per.CanUpdate) ;
            
bool b4 = (pType == PermissionType.Delete) && (! per.CanDelete) ;

            
bool b5 = false ;

            
if((pType != PermissionType.Browse) && (pType != PermissionType.Add) && (pType != PermissionType.Update) && (pType != PermissionType.Delete))
            {
                b5 
= ! this.permissionHelper.VerifyCustomPermission(per ,pType) ;
            }

            
if(b1 || b2 || b3 || b4 || b5)
            {
                
this.permissionHelper.OnHaveNoPermission(pType ,response) ;
                
return ;
            }

            destMethod.DynamicInvoke(args) ;
        }    
        
#endregion
    }

    Excute方法之所以重载是因为,如果条件满足,应用通常是两种反应:
(1)转到用户想进入的某个page
(2)执行用户希望的某个方法/操作

    实际上,紧紧使用第二个Excute已经可以包含一切了,第一个Excute是为了简化通常情况(进入的某个page)的操作。
    有了PermissionController,我们的工作会轻松很多,比如,在没有使用PermissionController的时候,我们经常这样做:
        private void DataGrid1_ItemCommand(object source, System.Web.UI.WebControls.DataGridCommandEventArgs e)
        {
            string id = e.Item.Cells[0].Text;
            if(e.CommandName == "ButtonUpdate")
            {
                 if(!SessionHelper.CreateInstance(this.Session).UserRoleCollection.AdminSubSystemPermission.CanUpdate)
                {
                    this.Response.Redirect("../ErrorInfoForm.aspx") ;
                    return ;
                }

                this.Response.Redirect("CQProjectPhaseForm.aspx?ID="+id);
            }
        }

    使用PermissionController后,主要这样就可以了:
        private void DataGrid1_ItemCommand(object source, System.Web.UI.WebControls.DataGridCommandEventArgs e)
        {
            string id = e.Item.Cells[0].Text;

            GeneralPermission gp = SessionHelper.CreateInstance(this.Session).UserRoleCollection.AdminSubSystemPermission ;
            if(e.CommandName == "ButtonUpdate")
            {
                 Global.PermissionController.Excute( gp,MGPPermissionType.Update ,this.Response ,"CQProjectPhaseForm.aspx?ID="+id) ;                
            }
    
    原来需要六行代码,现在一行就搞定了。(关于SessionHelper的信息,请参见 这里
    
    我们小结一下,看看使用PermissionController还会带来哪些好处:
(1)将原来分散的权限控制,全部集中起来了。
(2)应用中对权限控制所需的代码量大大减少。
(3)如果要改变系统针对权限不满足时所进行的动作,只要改变IPermissionHelper.OnHaveNoPermission方法的实现就可以了,而不是要到每个page中修改。
(4)扩展/修改权限类别非常方便。

    你可以把PermissionController的实现和相关接口定义拷贝到自己的类库中,然后在项目中使用。也可以直接使用 ESFramework(V0.1+)的ESFramework.Web.PermissionController类。

    欢迎你和我讨论关于权限控制的更多更好的解决方案, agilesoft@163.com

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值