相信每个涉及到用户的系统都有一套用户权限管理平台或者模块,用来维护用户以及在系统内的功能、数据权限,我们使用的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>