相信每个涉及到用户的系统都有一套用户权限管理平台或者模块,用来维护用户以及在系统内的功能、数据权限,我们使用的Activiti工作流引擎配套设计了包括 User、Group的Identify模块,怎么和业务数据同步呢,这个问题是每个新人必问的问题之一,下面介绍几种同步方案,最后总结比较。
如果你在考虑直接使用Activiti引擎的Identify模块作为系统的用户数据管理模块,您真是奇才~开个玩笑
参考IdentifyService接口Javadoc: http://www.activiti.org/javadocs/org/activiti/engine/IdentityService.html
importjava.util.List; importcom.foo.arch.entity.id.User; importcom.foo.arch.service.ServiceException; /** * 维护用户、角色、权限接口 * * @author HenryYan * */ publicinterface AccountService { /** * 添加用户并[同步其他数据库] * <ul> * <li>step 1: 保存系统用户,同时设置和部门的关系</li> * <li>step 2: 同步用户信息到activiti的identity.User,同时设置角色</li> * </ul> * * @param user 用户对象 * @param orgId 部门ID * @param roleIds 角色ID集合 * @param synToActiviti 是否同步到Activiti数据库,通过配置文件方式设置,使用属性:account.user.add.syntoactiviti * @throws OrganizationNotFoundException 关联用户和部门的时候从数据库查询不到哦啊部门对象 * @throws Exception 其他未知异常 */ publicvoid save(User user, Long orgId, List<long> roleIds, booleansynToActiviti) throws OrganizationNotFoundException, ServiceException, Exception; /** * 删除用户 * @param userId 用户ID * @param synToActiviti 是否同步到Activiti数据库,通过配置文件方式设置,使用属性:account.user.add.syntoactiviti * @throws Exception */ publicvoid delete(Long userId, booleansynToActiviti) throwsServiceException, Exception; /** * 同步用户、角色数据到工作流 * @throws Exception */ publicvoid synAllUserAndRoleToActiviti() throwsException; /** * 删除工作流引擎Activiti的用户、角色以及关系 * @throws Exception */ publicvoid deleteAllActivitiIdentifyData() throwsException; }
@Service @Transactional publicclass AccountServiceImpl implementsAccountService { /** * 保存用户信息,并且同步用户信息到activiti的identity.User和identify.Group * @param user 用户对象{@link User} * @param roleIds 用户拥有的角色ID集合 * @param synToActiviti 是否同步数据到Activiti * @see Role */ publicvoid saveUser(User user, List<long> roleIds, booleansynToActiviti) { String userId = ObjectUtils.toString(user.getId()); // 保存系统用户 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)); }elseif (activitiUsers.size() > 1) { String errorMsg = "发现重复用户:id="+ userId; logger.error(errorMsg); thrownew RuntimeException(errorMsg); }else{ newActivitiUser(user, roleIds); } } } /** * 添加工作流用户以及角色 * @param user 用户对象{@link User} * @param roleIds 用户拥有的角色ID集合 */ privatevoid newActivitiUser(User user, List<long> roleIds) { String userId = user.getId().toString(); // 添加用户 saveActivitiUser(user); // 添加membership addMembershipToIdentify(roleIds, userId); } /** * 添加一个用户到Activiti {@link org.activiti.engine.identity.User} * @param user 用户对象, {@link User} */ privatevoid 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的用户于组关系 * @param roleIds 角色ID集合 * @param userId 用户ID */ privatevoid 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()); } } /** * 更新工作流用户以及角色 * @param user 用户对象{@link User} * @param roleIds 用户拥有的角色ID集合 * @param activitiUser Activiti引擎的用户对象,{@link org.activiti.engine.identity.User} */ privatevoid updateActivitiData(User user, List<long> roleIds, org.activiti.engine.identity.User activitiUser) { String userId = user.getId().toString(); // 更新用户主体信息 cloneAndSaveActivitiUser(user, activitiUser); // 删除用户的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); } /** * 使用系统用户对象属性设置到Activiti User对象中 * @param user 系统用户对象 * @param activitiUser Activiti User */ privatevoid 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 publicvoid delete(Long userId, booleansynToActiviti, booleansynToChecking) throwsServiceException, Exception { // 查询需要删除的用户对象 User user = accountManager.getEntity(userId); if(user == null) { thrownew ServiceException("删除用户时,找不到ID为"+ userId + "的用户"); } /** * 同步删除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()); } // 删除本系统用户 accountManager.deleteUser(userId); // 删除考勤机用户 if(synToChecking) { checkingAccountManager.deleteEntity(userId); } } }
同步全部数据步骤:
删除Activiti的User、Group、Membership数据
复制Role对象数据到Group
复制用户数据以及Membership数据
publicclass ActivitiIdentifyCommonDao { protectedLogger logger = LoggerFactory.getLogger(getClass()); @Autowired privateJdbcTemplate jdbcTemplate; /** * 删除用户和组的关系 */ publicvoid deleteAllUser() { String sql = "delete from ACT_ID_USER"; jdbcTemplate.execute(sql); logger.debug("deleted from activiti user."); } /** * 删除用户和组的关系 */ publicvoid deleteAllRole() { String sql = "delete from ACT_ID_GROUP"; jdbcTemplate.execute(sql); logger.debug("deleted from activiti group."); } /** * 删除用户和组的关系 */ publicvoid deleteAllMemerShip() { String sql = "delete from ACT_ID_MEMBERSHIP"; jdbcTemplate.execute(sql); logger.debug("deleted from activiti membership."); } }
publicclass ActivitiIdentifyService extendsAbstractBaseService { @Autowired protectedActivitiIdentifyCommonDao activitiIdentifyCommonDao; /** * 删除用户和组的关系 */ publicvoid deleteAllUser() { activitiIdentifyCommonDao.deleteAllUser(); } /** * 删除用户和组的关系 */ publicvoid deleteAllRole() { activitiIdentifyCommonDao.deleteAllRole(); } /** * 删除用户和组的关系 */ publicvoid deleteAllMemerShip() { activitiIdentifyCommonDao.deleteAllMemerShip(); } }
publicclass AccountServiceImpl implementsAccountService { @Override publicvoid synAllUserAndRoleToActiviti() throwsException { // 清空工作流用户、角色以及关系 deleteAllActivitiIdentifyData(); // 复制角色数据 synRoleToActiviti(); // 复制用户以及关系数据 synUserWithRoleToActiviti(); } /** * 复制用户以及关系数据 */ privatevoid synUserWithRoleToActiviti() { List<user> allUser = accountManager.getAll(); for(User user : allUser) { String userId = user.getId().toString(); // 添加一个用户到Activiti saveActivitiUser(user); // 角色和用户的关系 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} */ privatevoid 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 publicvoid deleteAllActivitiIdentifyData() throwsException { activitiIdentifyService.deleteAllMemerShip(); activitiIdentifyService.deleteAllRole(); activitiIdentifyService.deleteAllUser(); } }
此方法覆盖 IdentifyService接口的默认实现类: org.activiti.engine.impl.IdentityServiceImpl。
读者可以根据现有的用户管理接口实现覆盖 IdentityServiceImpl的每个方法的默认实现,这样就等于放弃使用系列表:ACT_ID_。
此方法不再提供代码,请读者自行根据现有接口逐一实现接口定义的功能。
此方案和第二种类似,放弃使用系列表:ACT_ID_,创建同名的视图。
创建视图必须删除引擎自动创建的ACT_ID_*表,否则不能创建视图。
创建的视图要保证数据类型一致,例如用户的ACT_ID_MEMBERSHIP表的两个字段都是字符型,一般系统中都是用NUMBER作为用户、角色的主键类型,所以创建视图的时候要把数字类型转换为字符型。
在引擎配置中设置属性 dbIdentityUsed为 false即可。
<beanid="processEngineConfiguration"class="org.activiti.spring.SpringProcessEngineConfiguration"> ... <propertyname="dbIdentityUsed"ref="false"> ... </property></bean>
方案 一:不破坏、不修改源码, 面向接口编程, 推荐;
方案 二:放弃原有的Identify模块,使用自定义的实现,特殊情况可以使用此方式;
方案 三:不需要编写Java代码,只需要创建同名视图即可, 对于现有系统的集成、强烈 推荐;