@AsyncによるSpringセキュリティコンテキストの伝播

@Asyncを使用したSpring Securityコンテキストの伝播

1. 前書き

このチュートリアルでは、propagation of the Spring Security principal with @Async.__に焦点を当てます。

デフォルトでは、Spring Security AuthenticationはThreadLocalにバインドされているため、実行フローが@Asyncを使用して新しいスレッドで実行される場合、それは認証されたコンテキストにはなりません。

それは理想的ではありません–修正しましょう。

2. Mavenの依存関係

Spring Securityで非同期統合を使用するには、pom.xmldependenciesに次のセクションを含める必要があります。


    org.springframework.security
    spring-security-config
    4.2.1.RELEASE

Spring Securityの依存関係の最新バージョンはhereにあります。

3. @Asyncを使用したSpring Security Propagation

まず、簡単な例を書いてみましょう。

@RequestMapping(method = RequestMethod.GET, value = "/async")
@ResponseBody
public Object standardProcessing() throws Exception {
    log.info("Outside the @Async logic - before the async call: "
      + SecurityContextHolder.getContext().getAuthentication().getPrincipal());

    asyncService.asyncCall();

    log.info("Inside the @Async logic - after the async call: "
      + SecurityContextHolder.getContext().getAuthentication().getPrincipal());

    return SecurityContextHolder.getContext().getAuthentication().getPrincipal();
}

We want to check if the Spring SecurityContext is propagated to the new thread.最初に、非同期呼び出しの前にコンテキストをログに記録し、次に非同期メソッドを実行し、最後にコンテキストを再度ログに記録します。 asyncCall()メソッドには次の実装があります。

@Async
@Override
public void asyncCall() {
    log.info("Inside the @Async logic: "
      + SecurityContextHolder.getContext().getAuthentication().getPrincipal());
}

ご覧のとおり、非同期メソッドの新しいスレッド内でコンテキストを出力するのは1行のコードだけです。

4. SecurityContextHolder戦略の前

SecurityContextHolder戦略を設定する前に、@Asyncメソッド内のコンテキストにはnull値があります。

特に、非同期ロジックを実行すると、メインプログラムでAuthenticationオブジェクトをログに記録できますが、@Async内にログを記録すると、次のようになります。 null。 これはログ出力の例です:

web - 2016-12-30 22:41:58,916 [http-nio-8081-exec-3] INFO
  o.example.web.service.AsyncService -
  Outside the @Async logic - before the async call:
  [email protected]:
  Username: temporary; ...

web - 2016-12-30 22:41:58,921 [http-nio-8081-exec-3] INFO
  o.example.web.service.AsyncService -
  Inside the @Async logic - after the async call:
  [email protected]:
  Username: temporary; ...

  web - 2016-12-30 22:41:58,926 [SimpleAsyncTaskExecutor-1] ERROR
  o.s.a.i.SimpleAsyncUncaughtExceptionHandler -
  Unexpected error occurred invoking async method
  'public void org.example.web.service.AsyncServiceImpl.asyncCall()'.
  java.lang.NullPointerException: null

したがって、ご覧のとおり、エグゼキュータスレッド内では、プリンシパルが利用できないため、予想どおり、呼び出しはNPEで失敗します。

この動作を防ぐには、SecurityContextHolder.MODE_INHERITABLETHREADLOCAL戦略を有効にする必要があります。

SecurityContextHolder.setStrategyName(SecurityContextHolder.MODE_INHERITABLETHREADLOCAL);

5. SecurityContextHolder戦略の後

これで、外部にアクセスできるように、非同期スレッド内のプリンシパルにアクセスできるはずです。

実行してログ情報を確認し、それが当てはまることを確認しましょう。

web - 2016-12-30 22:45:18,013 [http-nio-8081-exec-3] INFO
  o.example.web.service.AsyncService -
  Outside the @Async logic - before the async call:
  [email protected]:
  Username: temporary; ...

web - 2016-12-30 22:45:18,018 [http-nio-8081-exec-3] INFO
  o.example.web.service.AsyncService -
  Inside the @Async logic - after the async call:
  [email protected]:
  Username: temporary; ...

web - 2016-12-30 22:45:18,019 [SimpleAsyncTaskExecutor-1] INFO
  o.example.web.service.AsyncService -
  Inside the @Async logic:
  [email protected]:
  Username: temporary; ...

そして、ここにあります–予想どおり、非同期エグゼキュータスレッド内に同じプリンシパルが表示されています。 **

6. ユースケース

SecurityContextが次のように伝播されることを確認したい場合の興味深いユースケースがいくつかあります。

  • 並列に実行でき、実行にかなりの時間がかかる可能性がある複数の外部リクエストを作成したい

  • ローカルで行う重要な処理がいくつかあり、外部リクエストはそれと並行して実行できます

  • その他は、たとえばメールを送信するなど、忘れがたいシナリオを表します

7. 結論

このクイックチュートリアルでは、伝播されたSecurityContext.を使用して非同期リクエストを送信するためのSpringサポートを紹介しました。プログラミングモデルの観点からは、新しい機能は一見シンプルに見えます。

複数のメソッド呼び出しが以前に同期的にチェーンされていた場合、非同期アプローチに変換するには、結果の同期が必要になる場合があることに注意してください。

この例は、over on GithubのMavenプロジェクトとしても利用できます。