2016-10-12 3 views
2

最初の実行可能ファイルがサブミットされると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; 
+1

現在のユーザーの正確な場所はわかりますか? 'SessionContext'または' Principal'? – flo

+0

2つ目の呼び出しでRunnableとサービスの両方で不正な値を持っていることが判明しました。 – Mike

答えて

1

あなたのProjectService@RequestScoped Beanにそれをカプセル化することによって、新たなExecutorServiceのを作成するには、「強制」でした。私は豚のコードを調べ、ExecutorServiceがコンテナによって管理されていないことを知りました。私はManagerExecutorServiceを作成し、SessionContextは適切に伝えられていました。

@Resource(lookup = "java:jboss/ee/concurrency/executor/customExecutor") 
private ManagedExecutorService es_; 
0

私はあなた@Inject@Dependent@Stateless BeanにExecutorServiceスコープという、問題があると思います。
@StatelessのBeanをプールして再利用することができますが、@Dependent CDI Beanは参照によって格納されるため、@Stateless Beanを再利用すると再作成されません。

ExecutorServiceの実装を知らないと、最初の実行時にコンテキストスレッドを作成し、コンテキストを調整せずに2番目の実行で再利用すると思います。私はこの問題を考え出し

@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); 
     }); 
    } 
} 
+0

私はこれを試しました。私はまだ注入されたSessionContextとPrincipalの両方で不正なプリンシパルを取得しています – Mike

関連する問題