Activiti 自定义用户管理和组管理
最近在研究如何自定义Activiti用户和组管理模块(Example:连接keystone用户管理模块,实际当中使用时当然可以将keystone转变为其他第三方用户管理模块),整理了一下心得,避免后人踩坑。
本文站立在activiti源代码中的activiti-webapp-rest2模块的肩膀上。
借助大神—Nadav Azaria— 2012年的文章《Activiti Authentication And Identity Management Tutorial》 开始全新的自定义之旅。
Activiti为我们提供的这样的入口,想想activiti的ldap模块的原理就应该清楚了,不过仍然疑惑的是如何才能构建activiti能够识别的class,并且如何向activiti引擎进行注册?
很显然,SessionFactory为我们提供了这样的接口。
首先,建立OwnUserManagerFactory和OwnGroupManagerFactory 均需继承activiti的SessionFactory。稍后我们将实现ManagerFactory中引用的class。
public class OwnUserManagerFactory implements SessionFactory {
private KeystoneConnection keystoneConnection;
public OwnUserManagerFactory (KeystoneConnection keystoneConnection) {
this.keystoneConnection = keystoneConnection;
}
public Class<?> getSessionType() {
return UserIdentityManager.class;
}
public Session openSession() {
return new OwnUserManager(this.getKeystoneConnection());
}
public KeystoneConnection getKeystoneConnection() {
return keystoneConnection;
}
public void setKeystoneConnection(KeystoneConnection keystoneConnection) {
this.keystoneConnection = keystoneConnection;
}
}
public class OwnGroupMagagerFactory implements SessionFactory {
private KeystoneConnection keystoneConnection;
public OwnGroupMagagerFactory(KeystoneConnection keystoneConnection) {
this.keystoneConnection = keystoneConnection;
}
@Override
public Class<?> getSessionType() {
return GroupIdentityManager.class;
}
@Override
public Session openSession() {
return new OwnGroupManager(this.getKeystoneConnection());
}
public KeystoneConnection getKeystoneConnection() {
return keystoneConnection;
}
public void setKeystoneConnection(KeystoneConnection keystoneConnection) {
this.keystoneConnection = keystoneConnection;
}
}
其次,建立OwnUserManager和OwnGroupManager,其中,我们需要自定义用户模块的查询方法,在findUserByQueryCriteria中,任何与第三方用户管理模块的通讯方式都是可以让我们自己实现的。
import org.activiti.engine.ActivitiException;
import org.activiti.engine.identity.User;
import org.activiti.engine.impl.Page;
import org.activiti.engine.impl.UserQueryImpl;
import org.activiti.engine.impl.persistence.entity.UserEntityManager;
public class OwnUserManager extends UserEntityManager {
private KeystoneConnection keystoneConnection;
public OwnUserManager(KeystoneConnection keystoneConnection) {
this.keystoneConnection = keystoneConnection;
}
@Override
public User createNewUser(String userId) {
return super.createNewUser(userId);
// throw new ActivitiException("User manager doesn't support creating a newe user");
}
@Override
public void insertUser(User user) {
super.insertUser(user);
// throw new ActivitiException("User manager doesn't support inserting a newe user");
}
@Override
public void updateUser(User updatedUser) {
super.updateUser(updatedUser);
// throw new ActivitiException("User manager doesn't support updating a newe user");
}
@Override
public User findUserById(String userId) {
return super.findUserById(userId);
// throw new ActivitiException("User manager doesn't support finding an user by id");
}
@Override
public void deleteUser(String userId) {
throw new ActivitiException("User manager doesn't support deleting a newe user");
}
@Override
public List<User> findUserByQueryCriteria(UserQueryImpl query, Page page) {
System.out.println(
"start to findUserByQueryCriteria.....................!!!!!!_----------------------------------------------------");
// use your own method or third party method...
return super.findUserByQueryCriteria(query, page);
}
@Override
public List<User> findUsersByNativeQuery(Map<String, Object> parameterMap, int firstResult, int maxResults) {
System.out.println(
"start to findUsersByNativeQuery.....................!!!!!!_----------------------------------------------------");
// use your own method or third party method...
return super.findUsersByNativeQuery(parameterMap, firstResult, maxResults);
}
@Override
public long findUserCountByQueryCriteria(UserQueryImpl query) {
return super.findUserCountByQueryCriteria(query);
// return findUserByQueryCriteria(query, null).size();
}
@Override
public Boolean checkPassword(String userId, String password) {
// now return true, means that ignoring the password verification
return true;
}
}
checkPassword 方法return true,即为跳过spring security用户验证,
public class OwnGroupManager extends GroupEntityManager {
private KeystoneConnection keystoneConnection;
public OwnGroupManager(KeystoneConnection keystoneConnection) {
this.keystoneConnection = keystoneConnection;
}
@Override
public void insertGroup(Group group) {
throw new ActivitiException("My group manager doesn't support inserting a new group");
}
@Override
public void updateGroup(Group updatedGroup) {
throw new ActivitiException("My group manager doesn't support updating a new group");
}
@Override
public void deleteGroup(String groupId) {
throw new ActivitiException("My group manager doesn't support deleting a new group");
}
@Override
public List<Group> findGroupByQueryCriteria(GroupQueryImpl query, Page page) {
// sometimes to implement how to query the Group
// return super.findGroupByQueryCriteria(query, page);
List<Group> groups = new ArrayList<Group>();
GroupEntity ge = new GroupEntity();
ge.setId("admin");
ge.setRevision(1);
ge.setName("Administrators");
ge.setType("security-role");
groups.add(ge);
return groups;
}
@Override
public long findGroupCountByQueryCriteria(GroupQueryImpl query) {
// TODO Auto-generated method stub
return super.findGroupCountByQueryCriteria(query);
}
@Override
public List<Group> findGroupsByUser(String userId) {
throw new ActivitiException("My group manager doesn't support finding a group");
}
}
然后,建立KeystoneConnection,当然可以换成第三方或者自定义的其它用户管理模块。
简简单单,它就是一个POJO。在findGroupByQueryCriteria和findUserByQueryCriteria可以调用基于KeystoneConnection的用户管理模块
public class KeystoneConnection {
private String protocal;
private String address;
private String port;
public KeystoneConnection() {
}
public KeystoneConnection(String protocal, String address, String port) {
this.protocal = protocal;
this.address = address;
this.port = port;
}
public String getKeystoneUrl(String url) {
return this.protocal + "://" + this.address + ":" + this.port + url;
}
// getter and setter
}
最后,需要告诉activiti引擎,如何才能识别我们在第一步中实现的两个工厂类,也就是注册的过程。我们扩展了原先conf中的ActivitiEngineConfiguration类,当然,如果在spring的xml文件中配置也是ok的。
@Configuration
public class ActivitiEngineConfiguration
@Bean(name = "processEngineConfiguration")
public ProcessEngineConfigurationImpl processEngineConfiguration() {
SpringProcessEngineConfiguration processEngineConfiguration = new SpringProcessEngineConfiguration();
// add following configuration
// load the custom manager session factory
List<SessionFactory> customSessionFactories = new ArrayList<SessionFactory>();
customSessionFactories.add(new OwnUserManagerFactory(keystoneConnection()));
customSessionFactories.add(new OwnGroupMagagerFactory(keystoneConnection()));
processEngineConfiguration.setCustomSessionFactories(customSessionFactories);
return processEngineConfiguration;
}
结论:
读一读源码,体会很深。
原理很简单,activiti的sessionFactory模块维护了一个
HashMap<SessionType, Session>
我们后来注册的这两个sub sessionFactory会覆盖activiti引擎初始化时所加载的其本身的关系映射,运行时每次读取的当然是我们自己定义的sessionFactory了。