相信每个涉及到用户的系统都有一套用户权限管理平台或者模块,用来维护用户以及在系统内的功能、数据权限,我们使用的Activiti工作流引擎配套设计了包括User、Group的Identify模块,怎么和业务数据同步呢,这个问题是每个新人必问的问题之一,下面介绍几种同步方案,最后总结比较。
参考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;
/**
* 维护用户、角色、权限接口
*
* @author HenryYan
*
*/
public
interface
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 其他未知异常
*/
public
void
save(User user, Long orgId, List<
long
> roleIds,
boolean
synToActiviti)
throws
OrganizationNotFoundException, ServiceException, Exception;
/**
* 删除用户
* @param userId 用户ID
* @param synToActiviti 是否同步到Activiti数据库,通过配置文件方式设置,使用属性:account.user.add.syntoactiviti
* @throws Exception
*/
public
void
delete(Long userId,
boolean
synToActiviti)
throws
ServiceException, Exception;
/**
* 同步用户、角色数据到工作流
* @throws Exception
*/
public
void
synAllUserAndRoleToActiviti()
throws
Exception;
/**
* 删除工作流引擎Activiti的用户、角色以及关系
* @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
@Transactional
public
class
AccountServiceImpl
implements
AccountService {
/**
* 保存用户信息,并且同步用户信息到activiti的identity.User和identify.Group
* @param user 用户对象{@link User}
* @param roleIds 用户拥有的角色ID集合
* @param synToActiviti 是否同步数据到Activiti
* @see Role
*/
public
void
saveUser(User user, List<
long
> roleIds,
boolean
synToActiviti) {
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
));
}
else
if
(activitiUsers.size() >
1
) {
String errorMsg =
"发现重复用户:id="
+ userId;
logger.error(errorMsg);
throw
new
RuntimeException(errorMsg);
}
else
{
newActivitiUser(user, roleIds);
}
}
}
/**
* 添加工作流用户以及角色
* @param user 用户对象{@link User}
* @param roleIds 用户拥有的角色ID集合
*/
private
void
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}
*/
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的用户于组关系
* @param roleIds 角色ID集合
* @param userId 用户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());
}
}
/**
* 更新工作流用户以及角色
* @param user 用户对象{@link User}
* @param roleIds 用户拥有的角色ID集合
* @param activitiUser Activiti引擎的用户对象,{@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();
// 更新用户主体信息
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
*/
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 {
// 查询需要删除的用户对象
User user = accountManager.getEntity(userId);
if
(user ==
null
) {
throw
new
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);
}
}
}
</role></group></
long
></
long
></
long
></org.activiti.engine.identity.user></
long
>
|
同步全部数据步骤:
删除Activiti的User、Group、Membership数据
复制Role对象数据到Group
复制用户数据以及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;
/**
* 删除用户和组的关系
*/
public
void
deleteAllUser() {
String sql =
"delete from ACT_ID_USER"
;
jdbcTemplate.execute(sql);
logger.debug(
"deleted from activiti user."
);
}
/**
* 删除用户和组的关系
*/
public
void
deleteAllRole() {
String sql =
"delete from ACT_ID_GROUP"
;
jdbcTemplate.execute(sql);
logger.debug(
"deleted from activiti group."
);
}
/**
* 删除用户和组的关系
*/
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;
/**
* 删除用户和组的关系
*/
public
void
deleteAllUser() {
activitiIdentifyCommonDao.deleteAllUser();
}
/**
* 删除用户和组的关系
*/
public
void
deleteAllRole() {
activitiIdentifyCommonDao.deleteAllRole();
}
/**
* 删除用户和组的关系
*/
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 {
// 清空工作流用户、角色以及关系
deleteAllActivitiIdentifyData();
// 复制角色数据
synRoleToActiviti();
// 复制用户以及关系数据
synUserWithRoleToActiviti();
}
/**
* 复制用户以及关系数据
*/
private
void
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}
*/
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>
|
引擎内部与数据库交互使用的是MyBatis,对不同的表的CRUD操作均由一个对应的实体管理器(XxxEntityManager,有接口和实现类),引擎的7个Service接口在需要CRUD实体时会根据接口获取注册的实体管理器实现类(初始化引擎时用Map对象维护两者的映射关系),并且引擎允许我们覆盖内部的实体管理器,查看源码后可以知道有关Identity操作的两个接口分别为:UserIdentityManager和GroupIdentityManager。
查看引擎配置对象ProcessEngineConfigurationImpl类可以找到一个名称为“customSessionFactories”的属性,该属性可以用来自定义SessionFactory(每一个XXxManager类都是一个Session<实现Session接口>,由SessionFactory来管理),为了能替代内部的实体管理器的实现我们可以自定义一个SessionFactory并注册到引擎。
这种自定义SessionFactory的方式适用于公司内部有独立的身份系统或者公共的身份模块的情况,所有和用户、角色、权限的服务均通过一个统一的接口获取,而业务系统则不保存这些数据,此时引擎的身份模块表就没必要存在(ACT_ID_*)。
下面是有关customSessionFactories的示例配置。
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
|
<
bean
id
=
"processEngineConfiguration"
class
=
"org.activiti.spring.SpringProcessEngineConfiguration"
>
...
<
property
name
=
"customSessionFactories"
>
<
list
>
<
bean
class
=
"me.kafeitu.activiti.xxx.CustomUserEntityManagerFactory"
>
<
property
name
=
"customUserEntityManager"
>
<
bean
class
=
"me.kafeitu.activiti.xxx.CustomUserEntityManager"
>
<
property
name
=
"CustomUserManager"
ref
=
"CustomUserManager"
>
</
property
></
bean
>
</
property
>
</
bean
>
<
bean
class
=
"me.kafeitu.activiti.xxx.CustomGroupEntityManagerFactory"
>
<
property
name
=
"customGroupEntityManager"
>
<
bean
class
=
"me.kafeitu.activiti.xxx.CustomGroupEntityManager"
>
<
property
name
=
"customRoleManager"
ref
=
"customRoleManager"
>
</
property
></
bean
>
</
property
>
</
bean
>
</
list
>
</
property
>
...
</
bean
>
<
bean
id
=
"customUserManager"
class
=
"me.kafeitu.activiti.xxx.impl.AiaUserManagerImpl"
>
<
property
name
=
"dao"
>
<
bean
class
=
"me.kafeitu.activiti.xxx.impl.CustomUserDaoImpl"
></
bean
>
</
property
>
<
property
name
=
"identityService"
ref
=
"identityService"
>
</
property
></
bean
>
<
bean
id
=
"customRoleManager"
class
=
"me.kafeitu.activiti.chapter19.identity.impl.AiaRoleManagerImpl"
>
<
property
name
=
"dao"
>
<
bean
class
=
"me.kafeitu.activiti.xxx.impl.CustomRoleDaoImpl"
></
bean
>
</
property
>
</
bean
>
|
以用户操作为例介绍一下如何自定义一个SessionFactory。
1
2
|