Java 如何避免ExecutorService重写Runnable的安全主体
当提交的第一个runnable是InjectExecutorService时,将为该runnable正确设置安全主体。随后提交的每个runnable都会获得原始用户的安全主体,而不是保持当前runnable。我的开发机器运行的是Wildfly 8.2 我正在为异步处理创建一个报告系统。我创建了一个服务,用于检查创建任务的用户,并确保只有该用户才能启动或完成任务。服务代码如下所示Java 如何避免ExecutorService重写Runnable的安全主体,java,multithreading,jakarta-ee,concurrency,cdi,Java,Multithreading,Jakarta Ee,Concurrency,Cdi,当提交的第一个runnable是InjectExecutorService时,将为该runnable正确设置安全主体。随后提交的每个runnable都会获得原始用户的安全主体,而不是保持当前runnable。我的开发机器运行的是Wildfly 8.2 我正在为异步处理创建一个报告系统。我创建了一个服务,用于检查创建任务的用户,并确保只有该用户才能启动或完成任务。服务代码如下所示 @Stateless public class ReportingService { //EE injectio
@Stateless
public class ReportingService {
//EE injection security context
@Resource
SessionContext context;
//CDI security Principal
@Inject
Principal principal;
//this method handles getting the username for EE injection or CDI
private String getCurrentUser() {
if (context != null) {
return context.getCallerPrincipal().getName();
}
if (principal != null) {
return principal.getName();
}
return null;
}
@TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
@Transactional
public void registerTask(String taskId) {
//Create task
//set task.submittedBy = getCurrentUser()
//persist task
//code has been omitted since it is working
}
private void validateCurrentUserRegisteredJob(String taskId) {
String user = //get user that created task with id = id from DB
String currentUser = getCurrentUser();
if (!user.equals(currentUser)) {
throw new EJBAccesException("Current user "+currentUser+" did not register task");
}
}
@TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
@Transactional
public void startTask(String taskId) {
validateCurrentUserRegisteredJob(taskid);
//retrieve task entity, set start time to now, and merge
}
...
}
下面是我的可运行代码
public TaskRunner() implements Runnable {
//CDI principal
@Inject
Principal principal;
@Inject
ReportingService rs;
private taskId;
public void setTaskId() {...}
public void run() {
log.debug("Inside Runner Current User: "+principal.getName());
rs.startTask(taskId);
....
}
}
以下是启动进程的REST端点调用的无状态Bean的代码
@Stateless
public ProjectService() {
@Inject
Instance<TaskRunner> taskRunner;
@Inject
ReportingService reportingService;
//ExecutorService that is create from Adam Bien's porcupine project
@Inject
@Dedicated
ExecutorService es;
//method that is called by rest enpoint to kick off
public void performAsynchAction(List<String> taskIds, ...rest of args...) {
taskIds.stream().forEach(t -> {
//registers task with user that made REST call
reportingService.registerTask(t);
TaskRunner runner = taskRunner.get();
runner.setTaskId(t);
log.debug("Created runner. Principal: "+runner.principal.getName());
es.submit(runner);
});
}
}
我第一次将Rest端点作为user1调用,并注册tasks:1-2。它们都按预期工作,我在日志中得到以下输出
Created runner. Principal: user1
Created runner. Principal: user1
Inside Runner Current User: user1
Inside Runner Current User: user1
下次我与user2进行相同的REST调用时,我在日志中得到以下输出
Created runner. Principal: user2
Inside Runner Current User: user1
EJBAccessException Current user user1 did not register task
第一次向ExecutorService提交Runnable时,Runnable的安全主体似乎设置正确。但是对于提交给ExecutorService的每个后续Runneable,它都使用第一个提交的runnable的安全主体。这是一个bug还是预期的行为?有人知道可能的工作吗
编辑:我发现我用来创建ExecutorService的豪猪项目没有被容器管理。当我切换到ManagedExecutorService时,SessionContext被正确地传播
@Resource(lookup = "java:jboss/ee/concurrency/executor/customExecutor")
private ManagedExecutorService es;
我认为问题在于,您
@将@依赖作用域执行服务注入@无状态bean。
@无状态
bean可以合并和重用,而@依赖
CDI bean通过引用存储,因此在重用@无状态
bean时不会重新创建
在不知道ExecutorService
的实现的情况下,我猜它会在第一次运行时创建上下文线程,并在第二次运行时在不调整上下文的情况下重用它们
您可以“强制”您的ProjectService
创建一个新的ExecutorService,方法是将其封装到@RequestScoped
bean中:
@RequestScoped
public class ESHolder {
@Inject
@Dedicated
ExecutorService eS;
public ExecutorService getES() {
return eS;
}
}
@Stateless
public ProjectService() {
// ...
@Inject
ESHolder esHolder;
public void performAsynchAction(List<String> taskIds, ...rest of args...) {
taskIds.stream().forEach(t -> {
// ...
esHolder.getES().submit(runner);
});
}
}
@RequestScoped
公共类ESHolder{
@注入
@专注的
遗嘱执行人;
公共服务getES(){
返回eS;
}
}
@无国籍
公共工程服务(){
// ...
@注入
埃舍尔德;
public void performAsynchAction(列出任务ID,…其他参数…){
taskIds.stream().forEach(t->{
// ...
esHolder.getES().submit(runner);
});
}
}
我解决了这个问题。我查看了豪猪代码,发现ExecutorService不是由容器管理的。我创建了一个ManagerExecutorService,然后SessionContext被正确地传播
@Resource(lookup = "java:jboss/ee/concurrency/executor/customExecutor")
private ManagedExecutorService es_;
你知道你的当前用户是从哪里来的吗SessionContext
或Principal
?事实证明,它们都被注入,并且在第二次调用的Runnable和service中都有不正确的值。我尝试了这个方法,但我仍然在注入的SessionContext和Principal中得到不正确的主体
@Resource(lookup = "java:jboss/ee/concurrency/executor/customExecutor")
private ManagedExecutorService es_;