最初の実行可能ファイルがサブミットされるとExecutorServiceが投入されると、セキュリティプリンシパルがその実行可能ファイルに対して正しく設定されます。後でサブミットされた各実行可能ファイルには、現在の実行可能ファイルを保持するのではなく、元のユーザーのセキュリティプリンシパルが与えられます。私の開発マシンはWildfly 8.2を実行しています。ExecutorServiceがRunnableのセキュリティプリンシパルをオーバーライドするのを避ける方法
私は非同期処理用のレポーティングシステムを作成しています。どのユーザーがタスクを作成したかをチェックし、そのユーザーだけがタスクを開始または完了できることを確認するサービスを作成しました。サービスのコードは次のとおりです。以下は
@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
}
...
}
は私のRunnableコード
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エンドポイントによって呼び出されるステートレスビーンからコード
@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 -> ProjectService.performAsynchAction(...)
-> reportingService.registerTask(...)
-> create CDI injected Runnable
-> submit runner to executor service
-> ExecutorService calls Runner.run()
-> rs.startTask(taskId)
私はRest1を初めてuser1として呼び出し、タスクを登録します:1-2。彼らはすべて期待どおりに動作し、私はログに次の出力を得ます。
Created runner. Principal: user1
Created runner. Principal: user1
Inside Runner Current User: user1
Inside Runner Current User: user1
私はuser2のと同じREST呼び出しを行うと、私は実行可能のセキュリティプリンシパルが正しく初めてaを設定されていることが表示されますログに
Created runner. Principal: user2
Inside Runner Current User: user1
EJBAccessException Current user user1 did not register task
を次の出力を取得し、次回RunnableはExecutorServiceに送信されます。ただし、ExecutorServiceに送信される各Runneableに対して、最初に送信された実行可能ファイルのセキュリティプリンシパルが使用されます。これはバグか意図された動作ですか?誰もが潜在的な回避策を知っていますか?
EDIT:ExecutorServiceを作成するために私が使用していたポーキュパインプロジェクトがコンテナによって管理されていないことがわかりました。 ManagedExecutorServiceに切り替えると、SessionContextが正しく伝播されていました。
@Resource(lookup = "java:jboss/ee/concurrency/executor/customExecutor")
private ManagedExecutorService es;
現在のユーザーの正確な場所はわかりますか? 'SessionContext'または' Principal'? – flo
2つ目の呼び出しでRunnableとサービスの両方で不正な値を持っていることが判明しました。 – Mike