OFBiz 初步 之 权限设计

简介

Apache Open For Business(Apache OFBiz) 是Apache开源的一个经典ERP项目。它提供了一套企业应用,用于集成以及自动化一些企业的“商业流程”。

从学习角度来看,它也是一个非常不错的企业级应用框架。这篇文章从OFBiz的权限设计这一切入点来谈谈OFBiz对于应用系统的权限设计。

设计思想简述

OFBiz采用的“安全组”(Security Group)来将"权限"跟"用户"联系起来。系统中有若干种权限,比如系统预置的权限、用户自定义的权限、资源的权限、操作的权限等等,这些权限会跟安全组建立关系(多对多的关系),而用户又与安全组建立关系(也是多对多的关系)。

其中,系统预置权限,是以XML配置的方式导入数据表的。这些配置文件通常的路径为{Component/Application baseDir}/data/XXXSecurityData.xml。这里有对整个权限设计相关表的初始化数据。

权限控制级别

OFBiz对于权限有如下几个控制级别:

登录级别

在每个Component的根目录下的ofbiz-component.xml文件下,有对于访问该component的“最基本的权限”定义。所谓最基本的权限,就是登录该component的用户需要至少拥有该文件内定义的权限才可以访问。示例:

见其中的“base-permission”属性。可以看到它包含了两个权限值——OFBTOOLS/FACILITY,这也意味着你必须同时拥有这两个权限才能访问该组件。而通常一个Component也会同时包含权限“OFBTOOLS”以及权限“COMPONNENT-NAME_VIEW”,这样配置的目的是OFBTOOLS用于对web app的访问进行控制,而COMPONNENT-NAME_VIEW用于控制浏览web app的信息。

component 菜单级别

component的顶级菜单显示的组件将只对登录过的用户(并且这些用户至少具有“WEBAPP-NAME_VIEW”或者“COMPONENT_NAME-ADMIN”权限)显示,这跟登录级别的限制相似。这种级别的访问控制实现在“appbar.ftl”中用以控制显示哪些应用程序的tab bar。

request(controller.xml)级别

这里有两个重要的参数,在每个component的webapp下的controller.xml中的每个request(<request-map)标签有一个security(<security)标签,包含了两个属性:

  • https:定义是否对该请求应用SSL加密
  • auth:定义是否需要登录才能执行该请求,因此只有在登录成功以及在其他级别上的安全检查通过后,该请求才会被执行

[javascript] view plain copy

  1. <!-- Request Mappings -->
  2. <request-map uri="MarketingReport">
  3. <security https="true" auth="true"/>
  4. <response name="success" type="view" value="MarketingReport"/>
  5. </request-map>

screen级别

在每个component下的widget文件夹下的screen配置文件中,<section节点下的<condition子节点,存在一个名为<if-has-permission的节点,它有两个属性:

  • permission:标识位于哪个component
  • action:标识执行的动作

permission_action正好构成一个权限,示例:

Freemarker模板片段级别

session变量:security总是存在于screen的上下文对象——context中,你可以在模板中使用已定义的Java方法:hasPermission;hasEntityPermission;hasRolePermission;

service定义级别

你可以定义专门的“Permission service”在不同的安全模式、不同的component中进行复用,这里你可以通过扩展ECA的规则来在其中插入权限验证。具体超出了本篇的范文可以参考示例`exampleGenericPermission`(example component下)

service编程级别

这里有两种方式:

  • Minilanguage:使用<check-permission 标签,注:Minilanguage是OFBiz特有的基于XML的“语言”。
  • Java:使用org.ofbiz.security.Security.API

记录级别

比如对于一个有特定约束的实体,一个基于它的查询,必须要具有特定的权限才能取得相应的结果

角色受限的(或者基于角色)权限(又称Party Roles)

同如上的记录级别,通过使用RoleType、PartyRole以及相关的实体(如ContentAndRole)来进行控制。

注:这里的角色倾向于业务规则,而完全不同于下面谈到的“角色”

安全角色

安全角色提供一个手段来将一个登录用户与一个特殊的OFBiz“元素”建立关联,举个例子,如果一个用户被分配有ORDERMGR_VIEW权限,而该用户又与一个特殊的团体有关联(假设称之为XYZ公司,该公司具有ORDERMGR_ROLE_UPDATE安全角色)。那么这么一组合将允许该用户浏览所有属于该公司的权限,并且可以只为该公司更新订单。

数据表结构设计

首先来看看OFBiz对于权限这块的表结构设计,这里一共牵扯到6个数据表:

  • SECURITY_GROUP
  • SECURITY_PERMISSION
  • SECURITY_GROUP_PERMISSION
  • USER_LOGIN_SECURITY_GROUP
  • PARTY_RELATIONSHIP
  • SECURITY_PERMISSION_AUTO_GRANT(不作讨论)

SECURITY_GROUP

这就是上面提到的“安全组”对应的数据表,用户通过从属于某个安全组来间接与权限产生关系。一个安全组可以简单得认为是包含有N个权限的集合。

SECURITY_PERMISSION

权限表,这里定义了系统中的所有权限。其中最主要需要关注的就是前两个字段:

  1. PERMISSION_ID:权限名称:通常以形如“Application_Operate”的形式进行定义(其中,Application表示具体的应用名称,Operate表示操作名称,常用的有CREATE/UPDATE/...)。当然了,也有一些特殊的命名方式比如:“MARKETING_VIEW”表示对MARKETING应用的页面拥有查看权限;"MANUAL_PAYMENT"表示人工支付的事务操作权限;"MARKETING_ADMIN"这里ADMIN作为后缀是一个特殊,表示它具有对MARKETING应用的所有操作权限。
  2. DESCRIPTION:对于PERMISSION_ID的简短描述

SECURITY_GROUP_PERMISSION

如上面在设计思路中所述,这是“Group”与“Permission”的多对多关系表。从语义上也不难理解:一个安全组可以拥有多个权限,一个权限也可以从属于多个安全组。

USER_LOGIN_SECURITY_GROUP

登录用户安全组,从表的命名方式上也不难看出,这也是一个多对多的关系表(注意观察主键定义)。我们可以看到,这张表并不只是将两张表的主键合起来作为联合主键,而是联合了“FROM_DATE”,三者联合作为主键。这里我们需要关注OFBiz数据表中普遍采用的一个设计模式——过期而非删除。也就是说,很多关系是有时效性的,这些时效性表现在"FROM_DATE"跟"THRU_DATE"两个字段上。如果发现当前记录已过期,那么就认为其无效,这就相当于我们传统意义上的“删除记录”操作。这种设计方式在关系庞杂的企业应用中可以避免在删除时被外键困扰而导致的各种异常以及数据一致性约束。

PARTY_RELATIONSHIP

可以看到该表中包含有一个SECURITY_GROUP_ID字段,用来关联一个安全组(该字段通常都为null)。因为之前安全组只是跟用户产生关联,而用户也是Party的一种,Party可以包含任何的个人、用户、机构等。PARTY_RELATIONSHIP可以用于描述任何两个事物之间的关系,而这种关系有时不仅仅是人,因此他们有时可能也需要拥有权限,而不仅仅是登录用户才需要拥有权限。

关键代码解读

看完了数据表设计,下面我们来看看代码实现,权限相关的代码被封装为"security"component,位于{Base_dir}/framework/security/src文件夹下,主要操作被抽象在名为Security的接口中,接口主要包含几个关键方法:

[java] view plain copy

  1. public Iterator<GenericValue> findUserLoginSecurityGroupByUserLoginId(String userLoginId);
  2. public boolean securityGroupPermissionExists(String groupId, String permission);
  3. public boolean hasPermission(String permission, HttpSession session);
  4. public boolean hasEntityPermission(String entity, String action, HttpSession session);
  5. public boolean hasRolePermission(String application, String action, String primaryKey, String role, HttpSession session);
  6. public void clearUserData(GenericValue userLogin);

其中,

第一个方法是根据用户Id查询用户权限组;

第二个方法是check某个安全组是否拥有某个权限

第三个方法是判断某用户是否拥有某个权限(这里用户的相关信息被封装在session中)

第四个方法判断用户是否拥有某个实体的操作权限

第五个方法判断是否拥有某角色的权限

第六个方法清楚跟用户相关的所有缓存数据(该方法由framework在用户退出登录的时候调用)

注:上面三个hasXXX 方法都有不同的重载

再来看看OFBiz中的默认实现(OFBizSecurity.Java)中的关键代码:

[java] view plain copy

  1. public boolean hasEntityPermission(String entity, String action, GenericValue userLogin) {
  2. if (userLogin == null) return false;
  3. // if (Debug.infoOn()) Debug.logInfo("hasEntityPermission: entity=" + entity + ", action=" + action, module);
  4. Iterator<GenericValue> iterator = findUserLoginSecurityGroupByUserLoginId(userLogin.getString("userLoginId"));
  5. GenericValue userLoginSecurityGroup = null;
  6. while (iterator.hasNext()) {
  7. userLoginSecurityGroup = iterator.next();
  8. // if (Debug.infoOn()) Debug.logInfo("hasEntityPermission: userLoginSecurityGroup=" + userLoginSecurityGroup.toString(), module);
  9. // always try _ADMIN first so that it will cache first, keeping the cache smaller
  10. if (securityGroupPermissionExists(userLoginSecurityGroup.getString("groupId"), entity + "_ADMIN"))
  11. return true;
  12. if (securityGroupPermissionExists(userLoginSecurityGroup.getString("groupId"), entity + action))
  13. return true;
  14. }
  15. return false;
  16. }

这是对于hasEntityPermission的最终实现,我们可以看到,它会首先尝试在entity中追加“ADMIN”字符串,也就是说,先查看超级权限,如果拥有超级权限,则直接认为拥有权限,否则才会去查看细粒度的具体权限。

[java] view plain copy

  1. public boolean hasRolePermission(String application, String action, String primaryKey, List<String> roles, GenericValue userLogin) {
  2. String entityName = null;
  3. EntityCondition condition = null;
  4. if (userLogin == null)
  5. return false;
  6. // quick test for special cases where were just want to check the permission (find screens)
  7. if (primaryKey.equals("") && roles == null) {
  8. if (hasEntityPermission(application, action, userLogin)) return true;
  9. if (hasEntityPermission(application + "_ROLE", action, userLogin)) return true;
  10. }
  11. Map<String, String> simpleRoleMap = OFBizSecurity.simpleRoleEntity.get(application);
  12. if (simpleRoleMap != null && roles != null) {
  13. entityName = simpleRoleMap.get("name");
  14. String pkey = simpleRoleMap.get("pkey");
  15. if (pkey != null) {
  16. List<EntityExpr> expressions = new ArrayList<EntityExpr>();
  17. for (String role: roles) {
  18. expressions.add(EntityCondition.makeCondition("roleTypeId", EntityOperator.EQUALS, role));
  19. }
  20. EntityConditionList<EntityExpr> exprList = EntityCondition.makeCondition(expressions, EntityOperator.OR);
  21. EntityExpr keyExpr = EntityCondition.makeCondition(pkey, primaryKey);
  22. EntityExpr partyExpr = EntityCondition.makeCondition("partyId", userLogin.getString("partyId"));
  23. condition = EntityCondition.makeCondition(exprList, keyExpr, partyExpr);
  24. }
  25. }
  26. return hasRolePermission(application, action, entityName, condition, userLogin);
  27. }

上面的代码可以看到,在方法内部会先尝试调用hasEntityPermission,如果没有权限,则尝试在application后面追加"_ROLE"字符串来查看角色权限是否拥有,如果拥有则,直接返回,否则才会根据相关的Entity-NameRole表继续查找。

Security_Group VS RBAC

我曾经在参与的一个项目中,接触过常用的RBAC(role based access control)权限设计思想。也曾转载过一篇OA的权限设计文章在OFBiz中弱化了角色的概念,而强化了“安全组”的概念。我认为是因为系统规模的不同造成的设计差异。RBAC常见于单一的系统设计,在单一的系统中,角色这个词定位准确而清晰;而在OFBiz中,它的目标是构建出一套ERP的平台(包含多个异构系统)。在跨越多个系统之上谈角色,反而变得模糊不清,导致混乱,但采用安全组的概念却不至于,安全组的概念使得权限的载体的粒度更细、更灵活,但同时也更为繁杂,因此其实OFBiz中还是有角色这个概念的(体现在权限中包含_ROLE的权限,可将其视为角色权限)。而常用的RBAC中通常也用到这种“安全组”的概念(一个特殊用户,需要分配有跨越多个角色的权限时,这时需要对该用户的权限进行定制化,这是就用得上类似的安全组的概念)。

因此我认为它们各适合不同的场景,但是可以互相结合的。

综述

这篇文章简要介绍了OFBiz中权限的设计,先阐述了OFBiz中权限的设计思想,然后概括了OFBiz中权限在哪些级别上起作用,紧接着分析了数据表设计,以及关键代码分析,最后对比了安全组的设计模式与RBAC的权限模型。

参考链接

http://blog.csdn.net/Liucheng417/article/details/49680185

时间: 2024-11-09 11:57:50

OFBiz 初步 之 权限设计的相关文章

ASP.NET网站权限设计实现(一)——使用PowerDesigner进行数据库设计

这里用PowerDesigner做一个初步的设计,后面可能会有修改. 1.启动PowerDesigner新建物理数据模型 2.工具栏 3.新建表模型 4.添加第一张表,可以双击表或右键菜单打开下面窗口 别忘了把Owner选上dbo,然后切换到Columns选项卡 完成后,表的模样是下图这副德行的 按此步骤依次添加其他表 6.添加表关系,选中工具箱的关系工具,由从表拖向主表,双击关系出现右侧窗口 7.配置数据库连接 8.创建数据库并在数据库中生成表,这里我是先在SQL Server Managem

权限设计

这次项目改版中 遇到权限设计的问题 因为对项目还不熟,再加上第一次做权限,就先做了个大概 拖来拖去 今天有了点想法 记录如下 (然后推荐一个在线逻辑图  https://www.processon.com 虽然访问速度比较慢,但是还是蛮实用的) 至于怎么想的,待续...

WisDom.Net 框架设计(五) 权限设计

WisDom.Net --权限设计 1.需求分析     基本在所有的管理系统中都离不开权限管理.可以这么说,权限管理是管理系统的核心所在. 权限管理说白一些就是每个人能够做什么,不能够做什么.可以说是一套规则.下面就说一下,在wisdom.net中的权限     1. 控制用户修改和删除数据.即 用户编辑和删除自己创建的数据,但是只能编辑和删除比自己权限小的人创建的数据     2. 模块的控制. 用户只能访问自己被授权访问的模块,不能访问其他模块     3. 用户被赋予不同的角色,各个角色

信息系统的权限设计

题记: 熟话说:男怕入错行,女怕嫁错郎! 掐指一算入行IT业,已7年的码农生涯.在今天来看,其实IT行业,要做点事情不没有想象中的那么难,可是为啥只知道上班.下班,有时面对自己无言以对啊!这么多年没给自己留下什么有形资产,实属遗憾! 苏联作家尼古拉·奥斯特洛夫斯基 在<钢铁是怎样炼成的>一书中,有一段话很是经典,让我们再次回顾一遍吧:人最宝贵的东西是生命.生命对人来说只有一次.因此,人的一生应当这样度过:当一个人回首往事时,不因虚度年华而悔恨,也不因碌碌无为而羞愧;这样,在他临死的时候, 能够

ASP.NET MVC +EasyUI 权限设计(二)环境搭建

请注明转载地址:http://www.cnblogs.com/arhat 今天突然发现博客园出问题了,老魏使用了PC,手机,平板都访问博客园了,都是不能正常的访问,原因是不能加载CSS,也就是不能访问common.cnblogs.com这个域名,一直出现"Aborted",非常的郁闷. 页面就是这样子的,不知道为什么,难道是不是我的3个终端有问题吧,还是园子的服务器有问题呢?还是路由器的问题呢?到现在这个问题还没解决,郁闷死了!弄得心情非常的不爽. 好吧,不在说这个问题了,开始我们的正

权限设计(上) - 数据库表设计

web权限设计,做权限目前有三种主流实现方式 第一种:手动实现 配置2个拦截器,一个是拦截是否登陆,一个是拦截url的权限,通过角色权限表的配置,把权限url的路径与访问资源的url进行匹配 第二种:spring-security实现,比较重,不推荐 第三章:shiro,目前spring已经舍弃自己的spring-security而采用shiro 先放出数据库设计(普通版,会增加字段),后面会详细介绍

模块管理常规功能自己定义系统的设计与实现(31--第三阶段 权限设计[1])

系统的各种权限设计(1) 视频解说在线观看:视频解说链接 http://i.youku.com/jfok1972 本系统的如今已能够设计的权限一共同拥有四种类型. 1.模块的操作权限:包含可浏览,增改删,附件的CRUD操作,审核.审批,附加功能的操作(这个前面忘了介绍了,在以下会介绍一下). 2.模块记录的可视权限:通俗的讲,就是哪些记录你能看,哪些记录你不能看. 3.字段的仅仅读权限:对于具有可新增和可改动权限的人.进一步限制哪些字段是仅仅读的. 4.字段的可视权限:哪些字段你不能看到. 以上

java用户角色权限设计

实现业务系统中的用户权限管理 B/S系统中的权限比C/S中的更显的重要,C/S系统因为具有特殊的客户端,所以访问用户的权限检测可以通过客户端实现或通过客户端+服务器检测实现,而B/S中,浏览器是每一台计算机都已具备的,如果不建立一个完整的权限检测,那么一个"非法用户"很可能就能通过浏览器轻易访问到B/S系统中的所有功能.因此B/S业务系统都需要有一个或多个权限系统来实现访问权限检测,让经过授权的用户可以正常合法的使用已授权功能,而对那些未经授权的"非法用户"将会将他

关于数据权限设计的一些想法

序言 在各种系统中,要保证数据对象的安全性以及易操作性,使企业的各业务部门.职能部门能够方便而且高效的协同工作,那么一个好的数据权限管理设计就成为一个关键的问题.虽然企业中各个单元的工作流程有所不同,处理的数据对象也有所不同,但是在组织结构.信息的处理方式上具有很多相同的地方,这就为设计数据对象的权限控制提供了一个抽象基础.数据权限的控制不同于一般的功能权限的控制,一般的功能权限指的是某个用户.角色或者是某个用户组能不能操作某种功能.而数据权限指的是某个用户.角色或者是某个用户组对某个数据对象的