相信每個(gè)涉及到用戶(hù)的系統都有一套用戶(hù)權限管理平臺或者模塊,用來(lái)維護用戶(hù)以及在系統內的功能、數據權限,我們使用的Activiti工作流引擎配套設計了包括User、Group的Identify模塊,怎么和業(yè)務(wù)數據同步呢,這個(gè)問(wèn)題是每個(gè)新人必問(wèn)的問(wèn)題之一,下面介紹幾種同步方案,最后總結比較。
如果你在考慮直接使用Activiti引擎的Identify模塊作為系統的用戶(hù)數據管理模塊,您真是奇才~開(kāi)個(gè)玩笑參考IdentifyService接口Javadoc:http://www.activiti.org/javadocs/org/activiti/engine/IdentityService.html
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 | package com.foo.arch.service.id;import java.util.List;import com.foo.arch.entity.id.User;import com.foo.arch.service.ServiceException;/** * 維護用戶(hù)、角色、權限接口 * * @author HenryYan * */public interface AccountService { /** * 添加用戶(hù)并[同步其他數據庫] * <ul> * <li>step 1: 保存系統用戶(hù),同時(shí)設置和部門(mén)的關(guān)系</li> * <li>step 2: 同步用戶(hù)信息到activiti的identity.User,同時(shí)設置角色</li> * </ul> * * @param user 用戶(hù)對象 * @param orgId 部門(mén)ID * @param roleIds 角色ID集合 * @param synToActiviti 是否同步到Activiti數據庫,通過(guò)配置文件方式設置,使用屬性:account.user.add.syntoactiviti * @throws OrganizationNotFoundException 關(guān)聯(lián)用戶(hù)和部門(mén)的時(shí)候從數據庫查詢(xún)不到哦啊部門(mén)對象 * @throws Exception 其他未知異常 */ public void save(User user, Long orgId, List<long> roleIds, boolean synToActiviti) throws OrganizationNotFoundException, ServiceException, Exception; /** * 刪除用戶(hù) * @param userId 用戶(hù)ID * @param synToActiviti 是否同步到Activiti數據庫,通過(guò)配置文件方式設置,使用屬性:account.user.add.syntoactiviti * @throws Exception */ public void delete(Long userId, boolean synToActiviti) throws ServiceException, Exception; /** * 同步用戶(hù)、角色數據到工作流 * @throws Exception */ public void synAllUserAndRoleToActiviti() throws Exception; /** * 刪除工作流引擎Activiti的用戶(hù)、角色以及關(guān)系 * @throws Exception */ public void deleteAllActivitiIdentifyData() throws Exception;}</long> |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 | @Service@Transactionalpublic class AccountServiceImpl implements AccountService { /** * 保存用戶(hù)信息,并且同步用戶(hù)信息到activiti的identity.User和identify.Group * @param user 用戶(hù)對象{@link User} * @param roleIds 用戶(hù)擁有的角色ID集合 * @param synToActiviti 是否同步數據到Activiti * @see Role */ public void saveUser(User user, List<long> roleIds, boolean synToActiviti) { String userId = ObjectUtils.toString(user.getId()); // 保存系統用戶(hù) accountManager.saveEntity(user); // 同步數據到Activiti Identify模塊 if (synToActiviti) { UserQuery userQuery = identityService.createUserQuery(); List<org.activiti.engine.identity.user> activitiUsers = userQuery.userId(userId).list(); if (activitiUsers.size() == 1) { updateActivitiData(user, roleIds, activitiUsers.get(0)); } else if (activitiUsers.size() > 1) { String errorMsg = "發(fā)現重復用戶(hù):id=" + userId; logger.error(errorMsg); throw new RuntimeException(errorMsg); } else { newActivitiUser(user, roleIds); } } } /** * 添加工作流用戶(hù)以及角色 * @param user 用戶(hù)對象{@link User} * @param roleIds 用戶(hù)擁有的角色ID集合 */ private void newActivitiUser(User user, List<long> roleIds) { String userId = user.getId().toString(); // 添加用戶(hù) saveActivitiUser(user); // 添加membership addMembershipToIdentify(roleIds, userId); } /** * 添加一個(gè)用戶(hù)到Activiti {@link org.activiti.engine.identity.User} * @param user 用戶(hù)對象, {@link User} */ private void saveActivitiUser(User user) { String userId = user.getId().toString(); org.activiti.engine.identity.User activitiUser = identityService.newUser(userId); cloneAndSaveActivitiUser(user, activitiUser); logger.debug("add activiti user: {}", ToStringBuilder.reflectionToString(activitiUser)); } /** * 添加Activiti Identify的用戶(hù)于組關(guān)系 * @param roleIds 角色ID集合 * @param userId 用戶(hù)ID */ private void addMembershipToIdentify(List<long> roleIds, String userId) { for (Long roleId : roleIds) { Role role = roleManager.getEntity(roleId); logger.debug("add role to activit: {}", role); identityService.createMembership(userId, role.getEnName()); } } /** * 更新工作流用戶(hù)以及角色 * @param user 用戶(hù)對象{@link User} * @param roleIds 用戶(hù)擁有的角色ID集合 * @param activitiUser Activiti引擎的用戶(hù)對象,{@link org.activiti.engine.identity.User} */ private void updateActivitiData(User user, List<long> roleIds, org.activiti.engine.identity.User activitiUser) { String userId = user.getId().toString(); // 更新用戶(hù)主體信息 cloneAndSaveActivitiUser(user, activitiUser); // 刪除用戶(hù)的membership List<group> activitiGroups = identityService.createGroupQuery().groupMember(userId).list(); for (Group group : activitiGroups) { logger.debug("delete group from activit: {}", ToStringBuilder.reflectionToString(group)); identityService.deleteMembership(userId, group.getId()); } // 添加membership addMembershipToIdentify(roleIds, userId); } /** * 使用系統用戶(hù)對象屬性設置到Activiti User對象中 * @param user 系統用戶(hù)對象 * @param activitiUser Activiti User */ private void cloneAndSaveActivitiUser(User user, org.activiti.engine.identity.User activitiUser) { activitiUser.setFirstName(user.getName()); activitiUser.setLastName(StringUtils.EMPTY); activitiUser.setPassword(StringUtils.EMPTY); activitiUser.setEmail(user.getEmail()); identityService.saveUser(activitiUser); } @Override public void delete(Long userId, boolean synToActiviti, boolean synToChecking) throws ServiceException, Exception { // 查詢(xún)需要刪除的用戶(hù)對象 User user = accountManager.getEntity(userId); if (user == null) { throw new ServiceException("刪除用戶(hù)時(shí),找不到ID為" + userId + "的用戶(hù)"); } /** * 同步刪除Activiti User Group */ if (synToActiviti) { // 同步刪除Activiti User List<role> roleList = user.getRoleList(); for (Role role : roleList) { identityService.deleteMembership(userId.toString(), role.getEnName()); } // 同步刪除Activiti User identityService.deleteUser(userId.toString()); } // 刪除本系統用戶(hù) accountManager.deleteUser(userId); // 刪除考勤機用戶(hù) if (synToChecking) { checkingAccountManager.deleteEntity(userId); } }}</role></group></long></long></long></org.activiti.engine.identity.user></long> |
同步全部數據步驟:
刪除Activiti的User、Group、Membership數據
復制Role對象數據到Group
復制用戶(hù)數據以及Membership數據
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | public class ActivitiIdentifyCommonDao { protected Logger logger = LoggerFactory.getLogger(getClass()); @Autowired private JdbcTemplate jdbcTemplate; /** * 刪除用戶(hù)和組的關(guān)系 */ public void deleteAllUser() { String sql = "delete from ACT_ID_USER"; jdbcTemplate.execute(sql); logger.debug("deleted from activiti user."); } /** * 刪除用戶(hù)和組的關(guān)系 */ public void deleteAllRole() { String sql = "delete from ACT_ID_GROUP"; jdbcTemplate.execute(sql); logger.debug("deleted from activiti group."); } /** * 刪除用戶(hù)和組的關(guān)系 */ public void deleteAllMemerShip() { String sql = "delete from ACT_ID_MEMBERSHIP"; jdbcTemplate.execute(sql); logger.debug("deleted from activiti membership."); }} |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | public class ActivitiIdentifyService extends AbstractBaseService { @Autowired protected ActivitiIdentifyCommonDao activitiIdentifyCommonDao; /** * 刪除用戶(hù)和組的關(guān)系 */ public void deleteAllUser() { activitiIdentifyCommonDao.deleteAllUser(); } /** * 刪除用戶(hù)和組的關(guān)系 */ public void deleteAllRole() { activitiIdentifyCommonDao.deleteAllRole(); } /** * 刪除用戶(hù)和組的關(guān)系 */ public void deleteAllMemerShip() { activitiIdentifyCommonDao.deleteAllMemerShip(); }} |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 | public class AccountServiceImpl implements AccountService {@Override public void synAllUserAndRoleToActiviti() throws Exception { // 清空工作流用戶(hù)、角色以及關(guān)系 deleteAllActivitiIdentifyData(); // 復制角色數據 synRoleToActiviti(); // 復制用戶(hù)以及關(guān)系數據 synUserWithRoleToActiviti(); } /** * 復制用戶(hù)以及關(guān)系數據 */ private void synUserWithRoleToActiviti() { List<user> allUser = accountManager.getAll(); for (User user : allUser) { String userId = user.getId().toString(); // 添加一個(gè)用戶(hù)到Activiti saveActivitiUser(user); // 角色和用戶(hù)的關(guān)系 List<role> roleList = user.getRoleList(); for (Role role : roleList) { identityService.createMembership(userId, role.getEnName()); logger.debug("add membership {user: {}, role: {}}", userId, role.getEnName()); } } } /** * 同步所有角色數據到{@link Group} */ private void synRoleToActiviti() { List<role> allRole = roleManager.getAll(); for (Role role : allRole) { String groupId = role.getEnName().toString(); Group group = identityService.newGroup(groupId); group.setName(role.getName()); group.setType(role.getType()); identityService.saveGroup(group); } } @Override public void deleteAllActivitiIdentifyData() throws Exception { activitiIdentifyService.deleteAllMemerShip(); activitiIdentifyService.deleteAllRole(); activitiIdentifyService.deleteAllUser(); }}</role></role></user> |
此方法覆蓋IdentifyService接口的默認實(shí)現類(lèi):org.activiti.engine.impl.IdentityServiceImpl。
讀者可以根據現有的用戶(hù)管理接口實(shí)現覆蓋IdentityServiceImpl的每個(gè)方法的默認實(shí)現,這樣就等于放棄使用系列表:ACT_ID_。
此方法不再提供代碼,請讀者自行根據現有接口逐一實(shí)現接口定義的功能。
此方案和第二種類(lèi)似,放棄使用系列表:ACT_ID_,創(chuàng )建同名的視圖。
創(chuàng )建視圖必須刪除引擎自動(dòng)創(chuàng )建的ACT_ID_*表,否則不能創(chuàng )建視圖。
創(chuàng )建的視圖要保證數據類(lèi)型一致,例如用戶(hù)的ACT_ID_MEMBERSHIP表的兩個(gè)字段都是字符型,一般系統中都是用NUMBER作為用戶(hù)、角色的主鍵類(lèi)型,所以創(chuàng )建視圖的時(shí)候要把數字類(lèi)型轉換為字符型。
在引擎配置中設置屬性isDbIdentityUsed為false即可。
方案一:不破壞、不修改源碼,面向接口編程,推薦;
方案二:放棄原有的Identify模塊,使用自定義的實(shí)現,特殊情況可以使用此方式;
方案三:不需要編寫(xiě)Java代碼,只需要創(chuàng )建同名視圖即可。
聯(lián)系客服