阿里云-云小站(无限量代金券发放中)
【腾讯云】云服务器、云数据库、COS、CDN、短信等热卖云产品特惠抢购

浅谈OFBiz之权限设计

120次阅读
没有评论

共计 9120 个字符,预计需要花费 23 分钟才能阅读完成。

简介

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 的用户需要至少拥有该文件内定义的权限才可以访问。示例:

浅谈 OFBiz 之权限设计

见其中的“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: 定义是否需要登录才能执行该请求,因此只有在登录成功以及在其他级别上的安全检查通过后,该请求才会被执行

<!– Request Mappings –>
    <request-map uri=”MarketingReport”>
        <security https=”true” auth=”true”/>
        <response name=”success” type=”view” value=”MarketingReport”/>
    </request-map>

screen 级别

在每个 component 下的 widget 文件夹下的 screen 配置文件中,<section 节点下的 <condition 子节点,存在一个名为 <if-has-permission 的节点,它有两个属性:
  • permission:标识位于哪个 component
  • action:标识执行的动作
permission_action 正好构成一个权限,示例:
浅谈 OFBiz 之权限设计

OFBiz 的详细介绍:请点这里
OFBiz 的下载地址:请点这里

相关阅读:

OFBiz 开发入门教程 http://www.linuxidc.com/Linux/2013-07/87655.htm

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

浅谈 OFBiz 之权限设计

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

 

SECURITY_PERMISSION

浅谈 OFBiz 之权限设计
 
权限表,这里定义了系统中的所有权限。其中最主要需要关注的就是前两个字段:
  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浅谈 OFBiz 之权限设计

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

USER_LOGIN_SECURITY_GROUP

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

PARTY_RELATIONSHIP

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

简介

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 的用户需要至少拥有该文件内定义的权限才可以访问。示例:

浅谈 OFBiz 之权限设计

见其中的“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: 定义是否需要登录才能执行该请求,因此只有在登录成功以及在其他级别上的安全检查通过后,该请求才会被执行

<!– Request Mappings –>
    <request-map uri=”MarketingReport”>
        <security https=”true” auth=”true”/>
        <response name=”success” type=”view” value=”MarketingReport”/>
    </request-map>

screen 级别

在每个 component 下的 widget 文件夹下的 screen 配置文件中,<section 节点下的 <condition 子节点,存在一个名为 <if-has-permission 的节点,它有两个属性:
  • permission:标识位于哪个 component
  • action:标识执行的动作
permission_action 正好构成一个权限,示例:
浅谈 OFBiz 之权限设计

OFBiz 的详细介绍:请点这里
OFBiz 的下载地址:请点这里

相关阅读:

OFBiz 开发入门教程 http://www.linuxidc.com/Linux/2013-07/87655.htm

关键代码解读

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

public Iterator<GenericValue> findUserLoginSecurityGroupByUserLoginId(String userLoginId);

public boolean securityGroupPermissionExists(String groupId, String permission);

public boolean hasPermission(String permission, HttpSession session);

public boolean hasEntityPermission(String entity, String action, HttpSession session);

public boolean hasRolePermission(String application, String action, String primaryKey, String role, HttpSession session);

public void clearUserData(GenericValue userLogin);

其中,

 

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

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

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

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

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

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

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

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

public boolean hasEntityPermission(String entity, String action, GenericValue userLogin) {
        if (userLogin == null) return false;

        // if (Debug.infoOn()) Debug.logInfo(“hasEntityPermission: entity=” + entity + “, action=” + action, module);
        Iterator<GenericValue> iterator = findUserLoginSecurityGroupByUserLoginId(userLogin.getString(“userLoginId”));
        GenericValue userLoginSecurityGroup = null;

        while (iterator.hasNext()) {
            userLoginSecurityGroup = iterator.next();

            // if (Debug.infoOn()) Debug.logInfo(“hasEntityPermission: userLoginSecurityGroup=” + userLoginSecurityGroup.toString(), module);

            // always try _ADMIN first so that it will cache first, keeping the cache smaller
            if (securityGroupPermissionExists(userLoginSecurityGroup.getString(“groupId”), entity + “_ADMIN”))
                return true;
            if (securityGroupPermissionExists(userLoginSecurityGroup.getString(“groupId”), entity + action))
                return true;
        }

        return false;
    }

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

public boolean hasRolePermission(String application, String action, String primaryKey, List<String> roles, GenericValue userLogin) {
        String entityName = null;
        EntityCondition condition = null;

        if (userLogin == null)
            return false;

        // quick test for special cases where were just want to check the permission (find screens)
        if (primaryKey.equals(“”) && roles == null) {
            if (hasEntityPermission(application, action, userLogin)) return true;
            if (hasEntityPermission(application + “_ROLE”, action, userLogin)) return true;
        }

        Map<String, String> simpleRoleMap = OFBizSecurity.simpleRoleEntity.get(application);
        if (simpleRoleMap != null && roles != null) {
            entityName = simpleRoleMap.get(“name”);
            String pkey = simpleRoleMap.get(“pkey”);
            if (pkey != null) {
                List<EntityExpr> expressions = new ArrayList<EntityExpr>();
                for (String role: roles) {
                    expressions.add(EntityCondition.makeCondition(“roleTypeId”, EntityOperator.EQUALS, role));
                }
                EntityConditionList<EntityExpr> exprList = EntityCondition.makeCondition(expressions, EntityOperator.OR);
                EntityExpr keyExpr = EntityCondition.makeCondition(pkey, primaryKey);
                EntityExpr partyExpr = EntityCondition.makeCondition(“partyId”, userLogin.getString(“partyId”));
                condition = EntityCondition.makeCondition(exprList, keyExpr, partyExpr);
            }

        }

        return hasRolePermission(application, action, entityName, condition, userLogin);
    }

上面的代码可以看到,在方法内部会先尝试调用 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 的权限模型。

引用

https://cwiki.apache.org/confluence/display/OFBTECH/OFBiz+security
正文完
星哥说事-微信公众号
post-qrcode
 0
星锅
版权声明:本站原创文章,由 星锅 于2022-01-20发表,共计9120字。
转载说明:除特殊说明外本站文章皆由CC-4.0协议发布,转载请注明出处。
【腾讯云】推广者专属福利,新客户无门槛领取总价值高达2860元代金券,每种代金券限量500张,先到先得。
阿里云-最新活动爆款每日限量供应
评论(没有评论)
验证码
【腾讯云】云服务器、云数据库、COS、CDN、短信等云产品特惠热卖中