现象:在某个审批方法中,直接使用taskService.addComment方法添加批注。但业务反馈,这条批注信息的提交用户是另一位同事!
js
taskService.addComment(taskId, pi.getProcessInstanceId(), comment.getFullMessage());
分析源码: 发现用户id:userId 是通过Authentication.getAuthenticatedUserId()获取!!!
js
public class AddCommentCmd implements Command<Comment> {
protected String taskId;
protected String processInstanceId;
protected String type;
protected String message;
public AddCommentCmd(String taskId, String processInstanceId, String message) {
this.taskId = taskId;
this.processInstanceId = processInstanceId;
this.message = message;
}
public AddCommentCmd(String taskId, String processInstanceId, String type, String message) {
this.taskId = taskId;
this.processInstanceId = processInstanceId;
this.type = type;
this.message = message;
}
@Override
public Comment execute(CommandContext commandContext) {
TaskEntity task = null;
// Validate task
if (taskId != null) {
task = CommandContextUtil.getTaskService().getTask(taskId);
if (task == null) {
throw new FlowableObjectNotFoundException("Cannot find task with id " + taskId, Task.class);
}
if (task.isSuspended()) {
throw new FlowableException(getSuspendedTaskException());
}
}
ExecutionEntity execution = null;
if (processInstanceId != null) {
execution = CommandContextUtil.getExecutionEntityManager(commandContext).findById(processInstanceId);
if (execution == null) {
throw new FlowableObjectNotFoundException("execution " + processInstanceId + " doesn't exist", Execution.class);
}
if (execution.isSuspended()) {
throw new FlowableException(getSuspendedExceptionMessage());
}
}
String processDefinitionId = null;
if (execution != null) {
processDefinitionId = execution.getProcessDefinitionId();
} else if (task != null) {
processDefinitionId = task.getProcessDefinitionId();
}
if (processDefinitionId != null && Flowable5Util.isFlowable5ProcessDefinitionId(commandContext, processDefinitionId)) {
Flowable5CompatibilityHandler compatibilityHandler = Flowable5Util.getFlowable5CompatibilityHandler();
return compatibilityHandler.addComment(taskId, processInstanceId, type, message);
}
// 注意此处, userId 是通过Authentication.getAuthenticatedUserId()获取!!!
String userId = Authentication.getAuthenticatedUserId();
CommentEntity comment = CommandContextUtil.getCommentEntityManager(commandContext).create();
comment.setUserId(userId);
comment.setType((type == null) ? CommentEntity.TYPE_COMMENT : type);
comment.setTime(CommandContextUtil.getProcessEngineConfiguration(commandContext).getClock().getCurrentTime());
comment.setTaskId(taskId);
comment.setProcessInstanceId(processInstanceId);
comment.setAction(Event.ACTION_ADD_COMMENT);
String eventMessage = message.replaceAll("\\s+", " ");
if (eventMessage.length() > 163) {
eventMessage = eventMessage.substring(0, 160) + "...";
}
comment.setMessage(eventMessage);
comment.setFullMessage(message);
CommandContextUtil.getCommentEntityManager(commandContext).insert(comment);
return comment;
}
protected String getSuspendedTaskException() {
return "Cannot add a comment to a suspended task";
}
protected String getSuspendedExceptionMessage() {
return "Cannot add a comment to a suspended execution";
}
}
继续分析 Authentication ,认证用户 是通过 AuthenticationContext 保存
js
public abstract class Authentication {
// 认证用户 是通过 AuthenticationContext 保存
private static AuthenticationContext authenticationContext = new UserIdAuthenticationContext();
public static void setAuthenticatedUserId(String authenticatedUserId) {
authenticationContext.setPrincipal(authenticatedUserId == null ? null : new UserIdPrincipal(authenticatedUserId));
}
public static String getAuthenticatedUserId() {
return authenticationContext.getAuthenticatedUserId();
}
public static AuthenticationContext getAuthenticationContext() {
return authenticationContext;
}
public static void setAuthenticationContext(AuthenticationContext authenticationContext) {
Authentication.authenticationContext = authenticationContext;
}
}
再继续分析AuthenticationContext
js
public class UserIdAuthenticationContext implements AuthenticationContext {
//此处是通过 ThreadLocal 将userid保存起来
private static ThreadLocal<Principal> authenticatedUserIdThreadLocal = new ThreadLocal();
public UserIdAuthenticationContext() {
}
public String getAuthenticatedUserId() {
Principal principal = (Principal)authenticatedUserIdThreadLocal.get();
return principal == null ? null : principal.getName();
}
public Principal getPrincipal() {
return (Principal)authenticatedUserIdThreadLocal.get();
}
public void setPrincipal(Principal principal) {
authenticatedUserIdThreadLocal.set(principal);
}
}
现在问题已经很清晰了。首先addComment方法添加的认证用户信息并不是通过task获取,而是从ThreadLocal中获取,再回头看代码,Controller层接收任务后,直接添加comment
同时结合日志,执行操作时,当前线程是tomcat线程池中的常驻线程。也就是说,该工作线程并不会因为执行结束而销毁,而且对应的ThreadLocal也一直未被回收。导致4个小时候添加用户时,存到库里的不是操作用户。
解决方法,在addcomment前,手动注入。
js
Authentication.setAuthenticatedUserId(task.getAssignee());