Fork me on GitHub
信息系统开发平台OpenExpressApp - 数据权限

需求

  数据权限总的目的:限制某些Role访问某些数据行,比如限制客户经理Role只能访问区域是北京的客户资料,区域是上海的客户资料,该Role就不能访问。

  一般场景:

  1. 普通员工可以对自己建立的业务对象有权限,而上级对所有下级建立的业务对象有权限
  2. 员工A对区域A的业务对象有权限,员工B对区域B的业务对象有权限
  3. 领导A可以看所有销售纪录,而大领导只关心金额超过1000K的销售纪录

   由于目前OEA对组织结构只是简单的支持,所以还不支持上下级等组织关系引起的数据权限,而先只考虑业务层面带来的数据权限,如上面的2、3

 

  下图为项目中一个具体案例,以下一个项目信息图,Project为项目,ProjectPBS为项目的一个计量维度,它关联到PBS。 ProjectPBS下有一些属性值(ProjectPBSPropertyValue),每个属性值关联到之前定义的属性(PBSProperty)。

  以下为系统的一个查询界面,它左边导航查询面板中用到了项目信息。现在需要控制,下面第二张图中的项目下拉数据需要考虑权限,选择项目后PBS显示也需要考虑权限,也就是说获取的列表数据必须根据权限过滤过。

  

数据权限功能

  根据需求,目前只支持对象集合的数据权限过滤,数据权限可能出现以下几个场景:

  • 对象级别
  1. 根据本对象的属性来过滤:如PBS名称,则在PBS对象设置数据权限表达式 this.Name = "建安工程"
  2. 根据子对象的属性来过滤:如项目PBS属性值(ProjectPBSPropertyValue)的某个属性(总建筑面积)的输入值>10000,则在ProjectPB对象设置数据权限表达式 this.ProjectPBSPropertyValues[Name="总建筑面积"].Value>10000
  3. 根据关联对象的属性来过滤:类似2
  4. 考虑相关对象(子对象或者关联对象本身)设置的数据权限,如项目PBS得到的PBS列表考虑PBS本身的数据权限设置
  • 属性级别
  1. 对某个属性不能查看
  2. 属性值控制:如金额>10000时不能查看等
  • 权限控制类型
    1. 可读
    2. 可写
    3. 自定义

  目前只支持对象级别,OEA的对象级别的权限是通过控制列表的形式来体现,目前支持前三种,只考虑对象本身设置的数据权限,权限控制类型暂考虑可读

实现思路

  目前实现数据权限基本上有以下方法:

  1. 通过动态生成SQL来解决,但这只能处理简单的权限设定  
  2. 通过对每个业务单独设计来解决,数据库增加相应表,但这会带来复杂性,不好统一管理

  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

  在代码中只需要根据业务对象实例生成一个表达式引擎 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);
}
}

todo

  由于目前项目中只需要应用到上面一个场景,由于时间关系,所以不可能全部支持,以下为主要的几个待做列表:

  1. 支持数据权限的其它场景(关联对象级别、属性级别)
  2. 表达式编辑器智能提示
  3. 支持自定义表达式编辑器
  4. 对于数据量大并且性能要求高,并且可以通过SQL where方式来过滤的情况下,支持where表达式,构造SQL

表达式引擎

  Now, 我要为了下午清醒的工作而午休了,关于表达式引擎,大家可以参考《开源 - 轻型的表达式引擎 Flee》

posted on 2010-01-28 15:59  HackerVirus  阅读(315)  评论(0编辑  收藏  举报