数据权限总的目的:限制某些Role访问某些数据行,比如限制客户经理Role只能访问区域是北京的客户资料,区域是上海的客户资料,该Role就不能访问。
一般场景:
由于目前OEA对组织结构只是简单的支持,所以还不支持上下级等组织关系引起的数据权限,而先只考虑业务层面带来的数据权限,如上面的2、3
下图为项目中一个具体案例,以下一个项目信息图,Project为项目,ProjectPBS为项目的一个计量维度,它关联到PBS。 ProjectPBS下有一些属性值(ProjectPBSPropertyValue),每个属性值关联到之前定义的属性(PBSProperty)。
以下为系统的一个查询界面,它左边导航查询面板中用到了项目信息。现在需要控制,下面第二张图中的项目下拉数据需要考虑权限,选择项目后PBS显示也需要考虑权限,也就是说获取的列表数据必须根据权限过滤过。
根据需求,目前只支持对象集合的数据权限过滤,数据权限可能出现以下几个场景:
目前只支持对象级别,OEA的对象级别的权限是通过控制列表的形式来体现,目前支持前三种,只考虑对象本身设置的数据权限,权限控制类型暂考虑可读
目前实现数据权限基本上有以下方法:
OEA主要考虑通过表达式来设置对象列表数据权限范围,一般的数据权限设定可以通过内置的表达式来设置,对于复杂的,和业务相关的,可以通过外部扩充表达式函数,由表达式引擎来解析,这样就可以处理复杂性,又能做到较好的统一管理。
对于复杂应用时,代码必须能够控制获取的数据是按照权限还是忽略权限,所以必须支持代码级别上的权限控制,也就是说需要写代码来控制,对于通用方法,可以通过代码生成器来生成。
由于数据权限设定后,需要在代码中进行相应修改,添加数据检查步骤,所以配置模块需要知道系统哪些对象运行设定数据权限。通过给业务对象的则DefaultObject增加InDataPermission属性来标识对象支持数据权限功能。
[DefaultObject( " 5D22DEBC-6EA7-45CD-8245-3D9855AE02A6 " , Catalog = " 指标管理 " ,
InDataPermission = true ), Label( " 项目信息 " )]
public partial class Project : GBusinessBase < Project >
修改前
private void DataPortal_Fetch()
{
IsReadOnly = false ;
RaiseListChangedEvents = false ;
using (var db = Helper.CreateDb())
{
IQuery q = db.Query();
var list = db.Select < Project > (q);
foreach (var item in list)
{
this .Add(Project.GetLazy(item.Id));
}
}
RaiseListChangedEvents = true ;
}
修改后:增加检查数据权限设定表达式步骤,这部分代码以后也可以通过代码生成器来生成
[DataPermission]
private void DataPortal_Fetch()
{
IsReadOnly = false ;
RaiseListChangedEvents = false ;
using (var db = Helper.CreateDb())
{
IQuery q = db.Query();
var list = db.Select < Project > (q);
if (list.Count == 0 ) return ;
// 考虑数据权限时,添加列表前需要执行数据权限表达式
DataPermissionExprParser oe = new DataPermissionExprParser(list[0 ]);
foreach (var item in list)
{
Project obj = Project.GetLazy(item.Id);
if (oe.CanRead(obj))
this .Add(obj);
}
}
RaiseListChangedEvents = true ;
}
注意:由于支持数据权限的业务对象会进行检查数据步骤,所以对系统性能造成一定影响,所以不需要细粒度控制到行级别数据权限的对象就不要进行数据权限部分的修改
在代码中只需要根据业务对象实例生成一个表达式引擎 DataPermissionExprParser,它会根据类型以及当前用户所在的角色来组合设定的当前对象的数据权限表达式,
public class DataPermissionExprParser : ObjectExprParser
{
string mergedExpr;
public DataPermissionExprParser( object owner)
: base (owner)
{
Guid businessObjectId = new Guid(owner.GetType().GetSingleAttribute < BusinessObjectAttribute > ().Id);
string [] exprs = (Csla.ApplicationContext.User.Identity as OEAIdentity).
GetDataPermissionExpr(businessObjectId);
if (exprs.Length == 0 )
mergedExpr = String.Empty;
else if (exprs.Length == 1 )
mergedExpr = exprs[ 0 ];
else
{
mergedExpr = string .Join( " ) OR ( " , exprs);
mergedExpr = String.Format( " ({0}) " , mergedExpr);
}
if (String.Empty != mergedExpr)
Compile(mergedExpr);
}
public bool CanRead( object owner)
{
return String.IsNullOrEmpty(mergedExpr) || ( bool )Evaluate(owner);
}
}
由于目前项目中只需要应用到上面一个场景,由于时间关系,所以不可能全部支持,以下为主要的几个待做列表:
Now, 我要为了下午清醒的工作而午休了,关于表达式引擎,大家可以参考《开源 - 轻型的表达式引擎 Flee》