相信每个涉及到用户的系统都有一套用户权限管理平台或者模块,用来维护用户以及在系统内的功能、数据权限,我们使用的Activiti工作流引擎配套设计了包括User、Group的Identify模块,怎么和业务数据同步呢,这个问题是每个新人必问的问题之一,下面介绍几种同步方案,最后总结比较。 如果你在考虑直接使用Activiti引擎的Identify模块作为系统的用户数据管理模块,您真是奇才~开个玩笑 方案一:调用IdentifyService接口完成同步 参考IdentifyService接口Javadoc:http://www.activiti.org/javadocs/org/activiti/engine/IdentityService.html 接口定义: ? 1 package com.foo.arch.service.id; 2 import java.util.List; 3 4 import com.foo.arch.entity.id.User; 5 import com.foo.arch.service.ServiceException; 6 7 /** 8 * 维护用户、角色、权限接口 9 * 10 * @author HenryYan 11 * 12 */ 13 public interface AccountService { 14 15 /** 16 * 添加用户并[同步其他数据库] 17 * <ul> 18 * <li>step 1: 保存系统用户,同时设置和部门的关系</li> 19 * <li>step 2: 同步用户信息到activiti的identity.User,同时设置角色</li> 20 * </ul> 21 * 22 * @param user 用户对象 23 * @param orgId 部门ID 24 * @param roleIds 角色ID集合 25 * @param synToActiviti 是否同步到Activiti数据库,通过配置文件方式设置,使用属性:account.user.add.syntoactiviti 26 * @throws OrganizationNotFoundException 关联用户和部门的时候从数据库查询不到哦啊部门对象 27 * @throws Exception 其他未知异常 28 */ 29 public void save(User user, Long orgId, List<long> roleIds, boolean synToActiviti) 30 throws OrganizationNotFoundException, ServiceException, Exception; 31 32 /** 33 * 删除用户 34 * @param userId 用户ID 35 * @param synToActiviti 是否同步到Activiti数据库,通过配置文件方式设置,使用属性:account.user.add.syntoactiviti 36 * @throws Exception 37 */ 38 public void delete(Long userId, boolean synToActiviti) throws ServiceException, Exception; 39 40 /** 41 * 同步用户、角色数据到工作流 42 * @throws Exception 43 */ 44 public void synAllUserAndRoleToActiviti() throws Exception; 45 46 /** 47 * 删除工作流引擎Activiti的用户、角色以及关系 48 * @throws Exception 49 */ 50 public void deleteAllActivitiIdentifyData() throws Exception; 51 } 52 </long> 同步单个接口实现片段: ? 1 @Service 2 @Transactional 3 public class AccountServiceImpl implements AccountService { 4 /** 5 * 保存用户信息,并且同步用户信息到activiti的identity.User和identify.Group 6 * @param user 用户对象{@link User} 7 * @param roleIds 用户拥有的角色ID集合 8 * @param synToActiviti 是否同步数据到Activiti 9 * @see Role 10 */ 11 public void saveUser(User user, List<long> roleIds, boolean synToActiviti) { 12 String userId = ObjectUtils.toString(user.getId()); 13 14 // 保存系统用户 15 accountManager.saveEntity(user); 16 17 // 同步数据到Activiti Identify模块 18 if (synToActiviti) { 19 UserQuery userQuery = identityService.createUserQuery(); 20 List<org.activiti.engine.identity.user> activitiUsers = userQuery.userId(userId).list(); 21 22 if (activitiUsers.size() == 1) { 23 updateActivitiData(user, roleIds, activitiUsers.get(0)); 24 } else if (activitiUsers.size() > 1) { 25 String errorMsg = "发现重复用户:id=" + userId; 26 logger.error(errorMsg); 27 throw new RuntimeException(errorMsg); 28 } else { 29 newActivitiUser(user, roleIds); 30 } 31 } 32 33 } 34 35 /** 36 * 添加工作流用户以及角色 37 * @param user 用户对象{@link User} 38 * @param roleIds 用户拥有的角色ID集合 39 */ 40 private void newActivitiUser(User user, List<long> roleIds) { 41 String userId = user.getId().toString(); 42 43 // 添加用户 44 saveActivitiUser(user); 45 46 // 添加membership 47 addMembershipToIdentify(roleIds, userId); 48 } 49 50 /** 51 * 添加一个用户到Activiti {@link org.activiti.engine.identity.User} 52 * @param user 用户对象, {@link User} 53 */ 54 private void saveActivitiUser(User user) { 55 String userId = user.getId().toString(); 56 org.activiti.engine.identity.User activitiUser = identityService.newUser(userId); 57 cloneAndSaveActivitiUser(user, activitiUser); 58 logger.debug("add activiti user: {}", ToStringBuilder.reflectionToString(activitiUser)); 59 } 60 61 /** 62 * 添加Activiti Identify的用户于组关系 63 * @param roleIds 角色ID集合 64 * @param userId 用户ID 65 */ 66 private void addMembershipToIdentify(List<long> roleIds, String userId) { 67 for (Long roleId : roleIds) { 68 Role role = roleManager.getEntity(roleId); 69 logger.debug("add role to activit: {}", role); 70 identityService.createMembership(userId, role.getEnName()); 71 } 72 } 73 74 /** 75 * 更新工作流用户以及角色 76 * @param user 用户对象{@link User} 77 * @param roleIds 用户拥有的角色ID集合 78 * @param activitiUser Activiti引擎的用户对象,{@link org.activiti.engine.identity.User} 79 */ 80 private void updateActivitiData(User user, List<long> roleIds, org.activiti.engine.identity.User activitiUser) { 81 82 String userId = user.getId().toString(); 83 84 // 更新用户主体信息 85 cloneAndSaveActivitiUser(user, activitiUser); 86 87 // 删除用户的membership 88 List<group> activitiGroups = identityService.createGroupQuery().groupMember(userId).list(); 89 for (Group group : activitiGroups) { 90 logger.debug("delete group from activit: {}", ToStringBuilder.reflectionToString(group)); 91 identityService.deleteMembership(userId, group.getId()); 92 } 93 94 // 添加membership 95 addMembershipToIdentify(roleIds, userId); 96 } 97 98 /** 99 * 使用系统用户对象属性设置到Activiti User对象中 100 * @param user 系统用户对象 101 * @param activitiUser Activiti User 102 */ 103 private void cloneAndSaveActivitiUser(User user, org.activiti.engine.identity.User activitiUser) { 104 activitiUser.setFirstName(user.getName()); 105 activitiUser.setLastName(StringUtils.EMPTY); 106 activitiUser.setPassword(StringUtils.EMPTY); 107 activitiUser.setEmail(user.getEmail()); 108 identityService.saveUser(activitiUser); 109 } 110 111 @Override 112 public void delete(Long userId, boolean synToActiviti, boolean synToChecking) throws ServiceException, Exception { 113 // 查询需要删除的用户对象 114 User user = accountManager.getEntity(userId); 115 if (user == null) { 116 throw new ServiceException("删除用户时,找不到ID为" + userId + "的用户"); 117 } 118 119 /** 120 * 同步删除Activiti User Group 121 */ 122 if (synToActiviti) { 123 // 同步删除Activiti User 124 List<role> roleList = user.getRoleList(); 125 for (Role role : roleList) { 126 identityService.deleteMembership(userId.toString(), role.getEnName()); 127 } 128 129 // 同步删除Activiti User 130 identityService.deleteUser(userId.toString()); 131 } 132 133 // 删除本系统用户 134 accountManager.deleteUser(userId); 135 136 // 删除考勤机用户 137 if (synToChecking) { 138 checkingAccountManager.deleteEntity(userId); 139 } 140 } 141 } 142 </role></group></long></long></long></org.activiti.engine.identity.user></long>
同步全部数据接口实现片段: 同步全部数据步骤: • 删除Activiti的User、Group、Membership数据 • 复制Role对象数据到Group • 复制用户数据以及Membership数据 ActivitiIdentifyCommonDao.java ? 1 public class ActivitiIdentifyCommonDao { 2 3 protected Logger logger = LoggerFactory.getLogger(getClass()); 4 5 @Autowired 6 private JdbcTemplate jdbcTemplate; 7 8 /** 9 * 删除用户和组的关系 10 */ 11 public void deleteAllUser() { 12 String sql = "delete from ACT_ID_USER"; 13 jdbcTemplate.execute(sql); 14 logger.debug("deleted from activiti user."); 15 } 16 17 /** 18 * 删除用户和组的关系 19 */ 20 public void deleteAllRole() { 21 String sql = "delete from ACT_ID_GROUP"; 22 jdbcTemplate.execute(sql); 23 logger.debug("deleted from activiti group."); 24 } 25 26 /** 27 * 删除用户和组的关系 28 */ 29 public void deleteAllMemerShip() { 30 String sql = "delete from ACT_ID_MEMBERSHIP"; 31 jdbcTemplate.execute(sql); 32 logger.debug("deleted from activiti membership."); 33 } 34 35 } ActivitiIdentifyService.java ? 1 public class ActivitiIdentifyService extends AbstractBaseService { 2 3 @Autowired 4 protected ActivitiIdentifyCommonDao activitiIdentifyCommonDao; 5 6 /** 7 * 删除用户和组的关系 8 */ 9 public void deleteAllUser() { 10 activitiIdentifyCommonDao.deleteAllUser(); 11 } 12 13 /** 14 * 删除用户和组的关系 15 */ 16 public void deleteAllRole() { 17 activitiIdentifyCommonDao.deleteAllRole(); 18 } 19 20 /** 21 * 删除用户和组的关系 22 */ 23 public void deleteAllMemerShip() { 24 activitiIdentifyCommonDao.deleteAllMemerShip(); 25 } 26 }
AccountServiceImpl.java ? 1 public class AccountServiceImpl implements AccountService { 2 @Override 3 public void synAllUserAndRoleToActiviti() throws Exception { 4 5 // 清空工作流用户、角色以及关系 6 deleteAllActivitiIdentifyData(); 7 8 // 复制角色数据 9 synRoleToActiviti(); 10 11 // 复制用户以及关系数据 12 synUserWithRoleToActiviti(); 13 } 14 15 /** 16 * 复制用户以及关系数据 17 */ 18 private void synUserWithRoleToActiviti() { 19 List<user> allUser = accountManager.getAll(); 20 for (User user : allUser) { 21 String userId = user.getId().toString(); 22 23 // 添加一个用户到Activiti 24 saveActivitiUser(user); 25 26 // 角色和用户的关系 27 List<role> roleList = user.getRoleList(); 28 for (Role role : roleList) { 29 identityService.createMembership(userId, role.getEnName()); 30 logger.debug("add membership {user: {}, role: {}}", userId, role.getEnName()); 31 } 32 } 33 } 34 35 /** 36 * 同步所有角色数据到{@link Group} 37 */ 38 private void synRoleToActiviti() { 39 List<role> allRole = roleManager.getAll(); 40 for (Role role : allRole) { 41 String groupId = role.getEnName().toString(); 42 Group group = identityService.newGroup(groupId); 43 group.setName(role.getName()); 44 group.setType(role.getType()); 45 identityService.saveGroup(group); 46 } 47 } 48 49 @Override 50 public void deleteAllActivitiIdentifyData() throws Exception { 51 activitiIdentifyService.deleteAllMemerShip(); 52 activitiIdentifyService.deleteAllRole(); 53 activitiIdentifyService.deleteAllUser(); 54 } 55 } 56 </role></role></user>
方案二:覆盖IdentifyService接口的实现 此方法覆盖IdentifyService接口的默认实现类:org.activiti.engine.impl.IdentityServiceImpl。 读者可以根据现有的用户管理接口实现覆盖IdentityServiceImpl的每个方法的默认实现,这样就等于放弃使用系列表:ACT_ID_。 此方法不再提供代码,请读者自行根据现有接口逐一实现接口定义的功能。
方案三:用视图覆盖同名的ACT_ID_系列表 此方案和第二种类似,放弃使用系列表:ACT_ID_,创建同名的视图。 1.删除已创建的ACT_ID_*表 创建视图必须删除引擎自动创建的ACT_ID_*表,否则不能创建视图。 2.创建视图: • ACT_ID_GROUP • ACT_ID_INFO • ACT_ID_MEMBERSHIP • ACT_ID_USER 创建的视图要保证数据类型一致,例如用户的ACT_ID_MEMBERSHIP表的两个字段都是字符型,一般系统中都是用NUMBER作为用户、角色的主键类型,所以创建视图的时候要把数字类型转换为字符型。 3.修改引擎默认配置 在引擎配置中设置属性isDbIdentityUsed为false即可。 ? 1 <bean class="org.activiti.spring.SpringProcessEngineConfiguration" id="processEngineConfiguration"> 2 ... 3 <property ref="false" name="isDbIdentityUsed"> 4 ... 5 </property> 6 </bean> 总结 • 方案一:不破坏、不修改源码,面向接口编程,推荐; • 方案二:放弃原有的Identify模块,使用自定义的实现,特殊情况可以使用此方式; • 方案三:不需要编写Java代码,只需要创建同名视图即可。 源文档 <http://www.kafeitu.me/activiti/2012/04/23/synchronize-or-redesign-user-and-role-for-activiti.html>