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

1前書き

このチュートリアルでは、 @Async __. を使ってSpring Securityプリンシパルの伝播に焦点を当てます。

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

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

2 Mavenの依存関係

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

<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-config</artifactId>
    <version>4.2.1.RELEASE</version>
</dependency>

Spring Securityの依存関係の最新版はhttps://search.maven.org/classic/#search%7Cga%7C1%7Cg%3A%22org.springframework.security%22[here]にあります。

3 @ Async を使ったSpringセキュリティの伝播

最初に簡単な例を書きましょう。

@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();
}
  • Spring SecurityContext が新しいスレッドに伝播されているかどうかをチェックしたい** 最初に、非同期呼び出しの前にコンテキストを記録し、次に非同期メソッドを実行し、最後に再度コンテキストを記録する。 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.baeldung.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.baeldung.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.baeldung.web.service.AsyncServiceImpl.asyncCall()'.
  java.lang.NullPointerException: null

したがって、ご覧のように、executorスレッドの内部では、予想どおりに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.baeldung.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.baeldung.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.baeldung.web.service.AsyncService -
  Inside the @Async logic:
 [email protected]:
  Username: temporary; ...

そして、ここにいます - 私たちが予想したとおり、非同期エグゼキュータスレッドの中にも同じプリンシパルがあります。 +

6. ユースケース

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

  • 並行して実行できる複数の外部リクエストを作成したい

実行にかなりの時間がかかる場合があります ** 私たちはローカルでそして私たちの外部でやるべきいくつかの重要な処理を持って

リクエストはそれと並行して実行できます。 ** otherは、例えば送信するなどの、忘れ去りシナリオを表します

Eメール

7. 結論

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

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

この例はMavenプロジェクトとしてhttps://github.com/eugenp/tutorials/tree/master/spring-security-rest[over on Github]でも入手できます。