歡迎您光臨本站 註冊首頁

使用JBoss jBPM實現流程訪問和執行的授權

←手機掃碼閱讀     火星人 @ 2014-03-09 , reply:0

  當今常見的BPM趨勢是集中化整個公司或公司內大部門的BPM執行.這意味著,單個BPM伺服器(集群)運行著整個公司的許多流程定義.這種方式的挑戰在 於,雖然BPM引擎(包括jBPM)提供了對於任務訪問的授權,但它們一般都不支持這些功能的授權:流程定義的查看和刪除,流程實例的啟動、結束、查看和刪除等.在這篇文章中,我們將描述如何對jBPM引擎進行擴展 (基於jBPM 4.3)來實現這一功能.

  整體實現方法

  整個實現方式相當直接了當——對於每個流程定義引入一組可以授權的用戶/用戶組(類似任務定義),作用於定義、實例和給定流程的歷史.此外,我們還想對給 定的用戶/用戶組支持多重授權級別——目前我們打算引入2個角色:「starter」和「user」.這裡的「starter」是允許對流程定義/實例 /歷史進行任何操作的角色,而「user」角色的許可權僅限於查詢流程/歷史.

  這種方式的實現需要對jBPM進行以下改造:

  ◆流程定義:

  給流程定義增加流程訪問許可權

  ◆流程部署:

  擴展當前的流程部署器,增加流程授權定義的解析和流程訪問列表的生成

  引入額外的類/資料庫表,存放每個流程定義的訪問許可權

  ◆流程執行(Execution)

  引入已授權命令(authorized command)——要求用戶經過授權才能執行的命令

  修改現有的jBPM中我們期望基於當前用戶證書進行授權的部分.這包括啟動、結束和刪除流程實例,以及刪除部署定義.

  修改現有的jBPM查詢,考慮現有用戶的證書.這包括部署和流程定義查詢、流程實例查詢,以及歷史流程實例、活動和細節的查詢.

  除了以上更改,我們還想擴展流程實例查詢,好讓用戶可以通過指定某些流程變數的值來縮小查詢結果.這種搜索的一個常見情況就是查詢「由我啟動的」流程.為 了確保這種查詢總是可用,我們更改了啟動流程實例命令的實現,顯式地把當前用戶ID加到了流程變數值的集合中.

  最后,為了支持多種用戶認證方法,我們實現了一個自定義的身份會話,它支持用程序來設置和訪問當前用戶的證書.其目的在於,把用戶證書(ID和參與的組) 的獲得和jBPM運行時對這種信息的使用分離開來.

  我們的實現利用了非常強大和靈活的jBPM 4的配置機制,它讓我們可以:

  通過擴展現有jBPM類,最小化了自定義代碼的數量,只實現我們擴展所需的額外功能

  將我們的擴展實現成可以與jBPM 4類庫一起使用的單獨jar包,無需對現有庫進行任何改變.

  在深入我們的實現細節之前,我們要討論一下我們大量使用的jBPM 4的配置.

  jBPM 4的配置機制

  jBPM的基礎是流程虛擬機(PVM)[2],它建立在自定義的依賴注入實現之上.依賴注入由非常強大的、基於XML的配置機制控制,這種機制用於創建標籤和預定義介面相關的特定實現之間的綁定 (binding).

  這種機制的核心是jbpm.wire.bindings.xml文件,它描述了[3] jBPM PVM的主要組件,包括:

  ◆基本類型

  ◆對象及引用

  ◆環境引用

  ◆Hibernate綁定

  ◆會話

  ◆服務

  ◆部署器

  ◆攔截器

  ◆等

  該文件是jBPM分發包的一部分.如果用戶想增加自己的綁定(binding),他可以創建jbpm.user.wire.bindings.xml描述 它們,而不用修改jbpm.wire.bindings.xml文件.

  這兩個文件會被jBPM PVM在啟動時讀入並解析,為定義在jbpm.cfg.xml中的基礎PVM執行(execution)配置而服務.jbpm.cfg.xml一般會包含 多個部分,描述了PVM執行的特定組件的配置.

  jBPM PVM由一組提供PVM功能的服務組成[4].主要的PVM服務包括:

  ◆倉儲服務,提供一組查看和管理部署倉儲的方法

  ◆執行服務,提供一組查看和管理運行中流程執行(execution)的方 法.

  ◆管理服務,提供一組查看和管理工作(job)的方法

  ◆任務服務,提供一組查看和管理用戶任務(task)的方法.

  ◆歷史服務,提供一組訪問運行中和已完成流程執行的歷史信息的方法.

  這組可用服務和實現這些服務的類(使用前面說的綁定)被配置成流程引擎的上下文.

  服務執行被實現成一組命令(command),它們作為服務方法執行的一部分被調用.命令的實際執行由命令服務控制.

  命令服務在命令服務上下文中被配置成一組攔截器,實現橫切關注點,環繞(around)命令調用(命令執行管線).預設的jBPM分發包在命令執行管線中 攜帶了以下攔截器:

  ◆重試(Retry)攔截器負責重試命令執行

  ◆環境(Environment)攔截器負責在必要時把jBPM上下文注入命 令執行中

  ◆事務(Transaction)攔截器負責介入命令調用的事務邊界劃分.

  攔截器是將jBPM移植到不同環境以及引入其他橫切關注點的核心機制.

  命令執行一般會利用環境,它也是可配置的.典型的環境組件有:

  ◆倉儲會話

  ◆DB會話

  ◆消息會話

  ◆定時器會話

  ◆歷史會話

  ◆郵件會話

  可以添加其他會話來擴展PVM的功能.

  最后,部署管理器配置允許指定一組部署器,它們依次執行,把業務流程部署到PVM.這種方法是的擴展流程定義可以通過實現額外的部署步驟完成,無需覆蓋 jBPM分發包自帶的部署器.

  整個PVM的架構如圖1示:


圖 1 PVM架構

  在流程定義中引入授權

  我們在圖2中看到,可以給流程定義添加任意屬性.利用這種擴展選項,我們現在定義以下流程屬性,描述授權策略:

  ◆starter-users,具有「starter」角色的用戶列表

  ◆starter-groups,具有 「starter」角色的組列表

  ◆user-users,具有「user」角色的用戶列表

  ◆user-groups,具 有「user」角色的組列表

  每個屬性的值是逗號分隔的組/用戶id列表.


圖 2 流程定義模式

  此外,我們還定義了一個特殊的用戶類型--「any」和兩個用戶組--「all」和「admin」.任何用戶,不論其真實ID是什麼,都是「any」用 戶.任何組,不論其ID是什麼,也都是「all」.最后,「admin」組的成員被認為是任意組的成員.

  流程授權定義由以下規則驅動:

  ◆如果user-users和user-groups都未被指定,則user-users=」all」

  ◆如果 starter-users和starte-groups都未被指定,則流程用戶被額外地分配「starter」角色.

  按照這個規則,清單1中的流程可以被任何人啟動和使用.

  1.<process package="com.navteq.jbpm"

  2. key="NO_AUTHORIZATION"

  3. name="Test Authorization not required"

  4. version="1"

  5. xmlns="http://jbpm.org/4.0/jpdl">

  6. <start g="68,14,48,48" name="start">

  7. <transition to="end"/>

  8. </start>

  9. <end g="78,383,48,48" name="end"/>

  10.</process>

  11.

  清單 1 沒有授權信息的流程定義

  清單2的流程可以被mark或tomcat組中的任何人使用和啟動.

  12.<process package="com.navteq.jbpm"

  13. key="AUTHORIZATION"

  14. name="Test Authorization Required"

  15. version="1"

  16. xmlns="http://jbpm.org/4.0/jpdl"

  17. user-users="mark"

  18. user-groups="tomcat">

  19. <start g="68,14,48,48" name="start" >

  20. <transition to="end"/>

  21. </start>

  22. <end g="78,383,48,48" name="end"/>

  23.</process>

  清單 2 具有用戶授權信息的流程定義

  我們引入了一個新類--ACL,針對給定流程 (processDefinitionID,processDefinitionKey,DeploymentID),它包含一個單獨的訪問列表(用戶或 組,以及類型);同時還引入了相應的Hibernate定義.

  圖3中,清單1的流程部署為具有兩個角色(「user」和「starter」)的用戶「any」創建了2個ACL;而在圖4中,清單2的流程部署將創建4 個--用戶「mark」和組「tomcat」,每個都具有2個角色:「user」和「starter」.


圖 3 無授權信息的流程的ACL


圖 4 有用戶授權信息的流程的ACL

  這些ACL的生成是通過引入額外的部署器完成的,它將在「標準」jBPM部署器之後運行,抽取上面描述的授權屬性,為給定流程構建ACL.

  保護jBPM命令

  我們採用了一種通用的方法來保護jBPM命令,包括實現用於定義命令所需授權信息的自定義的註解,以及處理這個註解的自定義的授權會話(命令攔截器)實現.

  授權註解(清單3)可以指定所需的用戶角色和表示某個流程的方法.

  [email protected](value=RetentionPolicy.RUNTIME)

  [email protected](value=ElementType.METHOD)

  3.public @interface AuthorizedCommand {

  4. /** Access type */

  5. public String role();

  6. String key();

  7.}

  8.

  清單 3 授權註解

  對於某個流程,用戶角色--「starter」或「user」--指向某個用戶應該擁有的角色[6].由於不同命令既可以引用部署ID,也可以引用流程ID或者流程鍵值,因此註解支持多種指定鍵值的方式,允許將合適的引用指定為鍵值.

  清單4的授權攔截器檢查是否有命令的方法被授權註解修飾.如果有,則執行適當的查詢,確定出哪些用戶和用戶組集合被授權給了這個命令,然後檢查當前用戶是 否屬於他們.

  9.…………

  10.

  [email protected]("unchecked")

  12.public void checkPermission(Command<?> command, EnvironmentImpl environment) {

  13. environment.setAuthenticatedUserId(environment.get(AuthorizationIdentitySession.class).getAuthenticatedUserId());

  14. for( Method method : command.getClass().getMethods()) {

  15. AuthorizedCommand sc = method.getAnnotation(AuthorizedCommand.class);

  16. if(sc != null){

  17. log.debug("Checking Class based Secured Function");

  18. String ID = environment.get(AuthorizationIdentitySession.class).getAuthenticatedUserId();

  19. Object value = null;

  20. try {

  21. log.debug("Checking authorization: " command.getClass().getName());

  22. Session session = environment.get(SessionImpl.class);

  23. value = method.invoke(command, (Object[])null);

  24. Query uQ = session.createQuery(userQuery.get(sc.key())).

  25. setString("role", sc.role()).setString("value",(String) value);

  26. Query gQ = session.createQuery(groupQuery.get(sc.key())).

  27. setString("role", sc.role()).setString("value", (String) value);

  28. List<String> userIds = (List<String>)uQ.list();

  29. List<String> groups = (List<String>)gQ.list();

  30. if(!isAuthorized(environment, userIds, groups))

  31. throw new AccessControlException(ID " attempted access to ProcessDefinition #" value);

  32. } catch (IllegalArgumentException e) {

  33. log.error("Caught " IllegalArgumentException.class, e);

  34. throw new AccessControlException(ID " attempted access to ProcessDefinition #" value);

  35. } catch (IllegalAccessException e) {

  36. log.error("Caught " IllegalAccessException.class, e);

  37. throw new AccessControlException(ID " attempted access to ProcessDefinition #" value);

  38. } catch (InvocationTargetException e) {

  39. log.error("Caught " InvocationTargetException.class, e);

  40. throw new AccessControlException(ID " attempted access to ProcessDefinition #" value);

  41. }

  42. }

  43. }

  44. return;

  45.}

  46.

  47.……………………

  48.

  49.public boolean isAuthorized(EnvironmentImpl env, List<String> authorizedUserIds, List<String> authorizedGroupIds) {

  50. AuthorizationIdentitySession identitySession = env.get(AuthorizationIdentitySession.class);

  51. if (authorizedUserIds.contains(AuthorizationIdentitySession.ANONYMOUS_USER_ID))

  52. return true;

  53. if (authorizedUserIds.contains(identitySession.getAuthenticatedUserId()) )

  54. return true;

  55. //check if any of userGroups is an authorized group. if so then return true

  56. List<Group> groups = identitySession.findGroupsByUser(identitySession.getAuthenticatedUserId());

  57. for(Group group : groups){

  58. String g = group.getId();

  59. //admin is allowed to execute any command

  60. if(g.equals(AuthorizationIdentitySession.ADMINISTRATORS_GROUP))

  61. return true;

  62. if(authorizedGroupIds.contains(g))

  63. return true;

  64. }

  65. return false;

  66.}

  67.

  清單 4 授權攔截器

  為了保護命令實現,我們創建了一個新類,它擴展了現有的命令,增加了一個帶註解的方法(清單5),返回給定命令可用的鍵值.

  [email protected](role = ACL.STARTER, key = NavteqAuthorizationSession.PROCESSID)

  69.public String getProcessDefinitionKey() {

  70. return processDefinitionId;

  71.}

  清單 5 給啟動流程實例命令引入授權信息

  根據所提議的方法,我們對下列命令進行了註解:

  ◆刪除部署

  ◆啟動流程實例

  ◆啟動最近的流程實例

  ◆結束流程實例

  ◆刪除流程實例

  擴展查詢

  引入授權意味著查詢結果應該只返回用戶被授權查看的信息[7].這可以通過擴展現有查詢的where語句實現(清單6).

  1.//check authorization

  2.String userId = EnvironmentImpl.getCurrent().getAuthenticatedUserId();

  3.List<Group> userGroups = EnvironmentImpl.getCurrent().get(IdentitySession.class).findGroupsByUser(userId);

  4.

  5.hql.append(", " ACL.class.getName() " as acl ");

  6.appendWhereClause("acl.deployment=deployment and acl.type='"

  7. ACL.STARTER

  8. "' ", hql);

  9.appendWhereClause("((acl.userId in "

  10. Utils.createHqlUserString(userId)

  11. ") or "

  12. "(acl.groupId in "

  13. Utils.createHqlGroupString(userGroups) "))

  14. ", hql);

  清單 6 為流程定義查詢提供授權支持的額外where語句

  額外的where語句是通過擴展現有查詢實現和覆蓋hlq方法實現的.

  按照這種方法,擴展了以下查詢:

  ◆部署查詢

  ◆流程定義查詢

  ◆流程實例查詢

  ◆歷史流程實例查詢

  ◆歷史活動 查詢

  ◆歷史細節查詢

  為了能夠增加字元串實例變數,以縮小查詢結果,我們還額外擴展了一個流程實例查詢.

  支持多種用戶管理方式

  以上給出的實現依賴使用執行環境來獲得當前用戶ID和使用IdentitySession來獲得用戶組成員關係.jBPM分發包提供了這個介面的2個實現:

  ◆IdentitySessionImpl,基於jBPM的用戶/組資料庫

  ◆JBossIdmIdentitySessionImpl, 基於JBoss Identity IDM組件

  不同於大量依賴其他技術的實現,對於我們的實現,我們決定把用戶ID/組的獲取同這些信息的保存分離開來,使之可以被其他的jBPM實現利用(圖5).


圖 5 用戶管理實現

  為了確保在設定和重新設定用戶證書的時候環境是可用的,我們把這兩個操作實現成了命令(清單7),這樣,藉助jBPM命令執行服務就可以正確設置執行環境.

  1.public static class SetPrincipalCommand extends AbstractCommand<Void> {

  2. private static final long serialVersionUID = 1L;

  3. private String userId;

  4. private String[] groups;

  5. public SetPrincipalCommand(String u, String...groups) {

  6. this.userId=u; this.groups=groups;

  7. }

  8. public Void execute(Environment environment) throws Exception {

  9. environment.get(AuthorizationIdentitySession.class).setPrincipal(userId,groups);

  10. return null;

  11. }

  12.}

  13.

  14.public static class ResetPrincipalCommand extends AbstractCommand<Void> {

  15. private static final long serialVersionUID = 1L;

  16. public Void execute(Environment environment) throws Exception {

  17. environment.get(AuthorizationIdentitySession.class).reset();

  18. return null;

  19. }

  20.}

  把新命令和查詢引入到jBPM執行中

  由於jBPM並沒有提供任何配置「命令--服務」關係的支持,為了能改變給定服務中的命令,就必須使用調用新命令的新服務實現覆蓋舊實現.清單8給出了一個使用新命令服務覆蓋歷史服務的例子.

  1.public class NavteqHistoryServiceImpl extends HistoryServiceImpl {

  2. @Override

  3. public HistoryActivityInstanceQuery createHistoryActivityInstanceQuery() {

  4. return new AuthorizedHistoryActivityInstanceQueryImpl(commandService);

  5. }

  6.

  7. @Override

  8. public HistoryDetailQuery createHistoryDetailQuery() {

  9. return new AuthorizedHistoryDetailQueryImpl(commandService);

  10. }

  11.

  12. @Override

  13. public HistoryProcessInstanceQuery createHistoryProcessInstanceQuery() {

  14. return newAuthorizedHistoryProcessInstanceQuery(commandService);

  15. }

  16.}

  17.

  清單 8 在歷史服務中引入授權命令

  總結

  本文給出的這部分實現[8]並沒有擴展JPDL,而是擴展了被jBPM支持的所有語言都使用的JBoss PVM[9].結果是,這些語言都能夠使用這個實現.


[火星人 ] 使用JBoss jBPM實現流程訪問和執行的授權已經有996次圍觀

http://coctec.com/docs/java/show-post-60286.html