碰到这个问题,我首先会想到在创建流程实例的时候肯定是赋值开启流程实例用户id的地方,但是经过查阅api,确实没有发现userId的传参地方,所以就经过了漫长的debug,发现问题
解答
Activiti 7 因为结合了Security的框架,所以很多api的用户信息,或者方法都需要首先经过用户登录才可以,这点不了解的,可以参考我之前写的帖子即可;
所以这边就可以首先登录,然后再正常的创建流程实例即可,用户信息就会自动带上。
查找问题方法
首先我没有在api中找到相关用户信息赋值的地方,然后推测用户信息十有八九,理论上也应该在创建流程实例的地方,所以就在这个方法入口,去debug一番:
public ProcessInstance startProcessInstanceByKey(String processDefinitionKey, String businessKey) {
return commandExecutor.execute(new StartProcessInstanceCmd<ProcessInstance>(processDefinitionKey, null, businessKey, null));
}
我这边是传入一个定义key以及一个businessKey来启动流程实例的,所以进入到这个方法,继续debug,会有很多很多一层又一层的
execute(CommandConfig config, Command<T> command)
这个方法,耐心点吧,大概需要至少遇到感觉十次左右的这个方法,差不多就会进入主题:
@Override
public ExecutionEntity createProcessInstanceExecution(ProcessDefinition processDefinition, String businessKey, String tenantId, String initiatorVariableName) {
ExecutionEntity processInstanceExecution = executionDataManager.create();
if (isExecutionRelatedEntityCountEnabledGlobally()) {
((CountingExecutionEntity) processInstanceExecution).setCountEnabled(true);
}
processInstanceExecution.setProcessDefinitionId(processDefinition.getId());
processInstanceExecution.setProcessDefinitionKey(processDefinition.getKey());
processInstanceExecution.setProcessDefinitionName(processDefinition.getName());
processInstanceExecution.setProcessDefinitionVersion(processDefinition.getVersion());
processInstanceExecution.setBusinessKey(businessKey);
processInstanceExecution.setScope(true); // process instance is always a scope for all child executions
// Inherit tenant id (if any)
if (tenantId != null) {
processInstanceExecution.setTenantId(tenantId);
}
String authenticatedUserId = Authentication.getAuthenticatedUserId();
processInstanceExecution.setStartTime(Context.getProcessEngineConfiguration().getClock().getCurrentTime());
processInstanceExecution.setStartUserId(authenticatedUserId);
// Store in database
insert(processInstanceExecution, false);
if (initiatorVariableName != null) {
processInstanceExecution.setVariable(initiatorVariableName, authenticatedUserId);
}
// Need to be after insert, cause we need the id
processInstanceExecution.setProcessInstanceId(processInstanceExecution.getId());
processInstanceExecution.setRootProcessInstanceId(processInstanceExecution.getId());
if (authenticatedUserId != null) {
getIdentityLinkEntityManager().addIdentityLink(processInstanceExecution, authenticatedUserId, null, IdentityLinkType.STARTER);
}
// Fire events
if (getEventDispatcher().isEnabled()) {
getEventDispatcher().dispatchEvent(ActivitiEventBuilder.createEntityEvent(ActivitiEventType.ENTITY_CREATED, processInstanceExecution));
}
return processInstanceExecution;
}
如果你能debug到这个方法,那么恭喜你,终于修成正果,仔细看下有这样的方法:
String authenticatedUserId = Authentication.getAuthenticatedUserId();
processInstanceExecution.setStartTime(Context.getProcessEngineConfiguration().getClock().getCurrentTime());
processInstanceExecution.setStartUserId(authenticatedUserId);
他从Authentication中获取到userId,然后赋值,根据类名,我们大概推测应该就跟权限相关了吧,那么进入这个类看下:
public abstract class Authentication {
static ThreadLocal<String> authenticatedUserIdThreadLocal = new ThreadLocal<String>();
public static void setAuthenticatedUserId(String authenticatedUserId) {
authenticatedUserIdThreadLocal.set(authenticatedUserId);
}
public static String getAuthenticatedUserId() {
return authenticatedUserIdThreadLocal.get();
}
}
发现维护了一个线程变量,然后有个get,set方法,我们看下set方法都有哪里在使用:
其实就是在我们自己写的类里面的一个赋值方法:
org.activiti.engine.impl.identity.Authentication.setAuthenticatedUserId(userId);
下面给个这个类的所有代码:
@Component
public class SecurityUtil {
private Logger logger = LoggerFactory.getLogger(SecurityUtil.class);
private static InMemoryUserDetailsManager inMemoryUserDetailsManager;
//查询实例
public static InMemoryUserDetailsManager findInstance(){
if(inMemoryUserDetailsManager==null){
inMemoryUserDetailsManager = new InMemoryUserDetailsManager();
}
return inMemoryUserDetailsManager;
}
public void createUser(String userId) {
inMemoryUserDetailsManager = findInstance();
if(inMemoryUserDetailsManager.userExists(userId)) {
inMemoryUserDetailsManager.deleteUser(userId);
}
List<SimpleGrantedAuthority> roles = new ArrayList<SimpleGrantedAuthority>(){{
add(new SimpleGrantedAuthority("ROLE_ACTIVITI_USER"));
add(new SimpleGrantedAuthority("GROUP_activitiTeam"));
}};
inMemoryUserDetailsManager.createUser(new User(userId, passwordEncoder().encode("password"),roles));
}
public void logInAs(String userId) {
createUser(userId);
UserDetails user = findInstance().loadUserByUsername(userId);
if (user == null) {
throw new IllegalStateException("User " + userId + " doesn't exist, please provide a valid user");
}
logger.info("> Logged in as: " + userId);
SecurityContextHolder.setContext(new SecurityContextImpl(new Authentication() {
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return user.getAuthorities();
}
@Override
public Object getCredentials() {
return user.getPassword();
}
@Override
public Object getDetails() {
return user;
}
@Override
public Object getPrincipal() {
return user;
}
@Override
public boolean isAuthenticated() {
return true;
}
@Override
public void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException {
}
@Override
public String getName() {
return user.getUsername();
}
}));
org.activiti.engine.impl.identity.Authentication.setAuthenticatedUserId(userId);
}
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
这里是我自己写的,activiti7与springboot security结合的地方,首先我们登录时候要经过这个安全框架,验证完之后,就会把用户信息存储在activiti的变量中一份。
完结--