概要
Spring Securityの認証成功/失敗イベントをハンドリングする方法についてまとめた。
前提
以下の記事の続きとなる。
概要 Spring Securityの認証機能に、パスワードのハッシュ化機能を導入する方法についてまとめた。 前提 以下の記事の続きとなる。
認証イベント仕様
Spring SecurityはSpring Frameworkが提供するイベント制御の仕組みを利用して、認証成功/失敗イベントを通知する。
この仕組みを活用することで、認証処理に以下のような拡張機能を柔軟に追加できる。
・パスワードの連続失敗回数をカウントし、閾値を超えた場合にアカウントロック制御を行う
認証イベント発行~伝播までのざっくりとした流れは以下のようになる。

②AuthenticationFilterにて、リクエストからユーザー名・パスワードなどの認証入力情報を取得する
③AuthenticationManagerを呼び出し、取得した認証入力情報を渡して認証処理を委譲する
④認証方式に対応するAuthenticationProviderが呼び出され、入力情報をもとに認証処理を実行する
⑤認証成功時はAuthenticationオブジェクトが返却され、失敗時はAuthenticationExceptionがスローされる
⑥認証結果に応じて、AuthenticationManagerがAuthenticationEventPublisherを通じて認証イベントを発行する
・成功時:AuthenticationSuccessEvent
・失敗時:例外種別に応じたAuthenticationFailureXXXEvent
⑦ApplicationEventPublisherが発行されたイベントをアプリケーション内部に通知する
⑧ApplicationListenerMethodAdapterが、イベントに対応するEventListenerアノテーションが付与されたメソッドを呼ぶ
実装
認証イベントに対応したイベントリスナーを追加する。
認証成功イベント
認証に成功した場合、Spring Securityは主に以下のイベントを発行する。
| イベントクラス | 説明 |
|---|---|
| AuthenticationSuccessEvent | AuthenticationManagerによる認証処理が成功したことを通知する。 このイベントをハンドリングすると、正しい認証情報がリクエストされたことを検知可能。 ※後続の認証処理にてエラーが発生する可能性はある |
| InteractiveAuthenticationSuccessEvent | インタラクティブ(フォームなどのUI操作による)なログイン認証がすべて成功したことを通知する。 このイベントをハンドリングすると、画面遷移を除いたすべての認証処理が成功したことを検知可能。 |
リスナー定義
認証成功時のイベントをハンドリングするイベントリスナーを作成する。
LoginSuccessListener.java
package com.example.prototype.biz.security.listener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.event.EventListener;
import org.springframework.security.authentication.event.AuthenticationSuccessEvent;
import org.springframework.security.authentication.event.InteractiveAuthenticationSuccessEvent;
import org.springframework.stereotype.Component;
@Component
public class LoginSuccessListener {
private static final Logger logger = LoggerFactory.getLogger(LoginSuccessListener.class);
@EventListener
public void handleAuthenticationSuccessEvent(AuthenticationSuccessEvent event) {
logger.info("\n★★ログイン成功: AuthenticationSuccessEventイベント★★\n");
}
@EventListener
public void handleInteractiveAuthenticationSuccessEvent(InteractiveAuthenticationSuccessEvent event) {
logger.info("\n★★ログイン成功: InteractiveAuthenticationSuccessEventイベント★★");
}
}
@EventListener
public void handleAuthenticationSuccessEvent(AuthenticationSuccessEvent event) {
@EventListener
public void handleInteractiveAuthenticationSuccessEvent(InteractiveAuthenticationSuccessEvent event) {
メソッドに「org.springframework.context.event.EventListener」アノテーションを定義して
ハンドリングしたいイベントを引数に指定することで、自動でハンドリングできる。
認証失敗イベント
認証に失敗した場合、Spring Securityは様々な認証失敗ケースにあったイベントを発行する。
基本的にはすべての認証失敗イベントの基底クラスである、AbstractAuthenticationFailureEventをハンドリングすればOK。
| イベントクラス | 説明 |
|---|---|
| AbstractAuthenticationFailureEvent | すべての認証失敗イベントの抽象基底クラス。 このイベントをハンドリングすることで、あらゆる認証失敗ケースを一括検知可能。 |
リスナー定義
認証失敗時のイベントをハンドリングするイベントリスナーを作成する。
LoginFailureListener.java
package com.example.prototype.biz.security.listener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.event.EventListener;
import org.springframework.security.authentication.event.AbstractAuthenticationFailureEvent;
import org.springframework.stereotype.Component;
@Component
public class LoginFailureListener {
private static final Logger logger = LoggerFactory.getLogger(LoginFailureListener.class);
@EventListener
public void handleAbstractAuthenticationFailureEvent(AbstractAuthenticationFailureEvent event) {
// ログイン失敗ユーザーのDB更新、ログ出力など
logger.info("\n★★ログイン失敗(認証例外イベント): {}★★\n", event.getException().getClass());
}
}
まとめ
☑ Spring Frameworkのイベント伝播の仕組みを利用することで、Spring Securityの認証結果イベント(成功/失敗)を検知できる
☑ イベントを検知するには、ルートアプリケーションコンテキスト配下にイベントリスナー用のBeanを定義する必要がある
☑ メソッドに@EventListenerを付与して引数に検知したいイベントクラスを指定することで、イベントをハンドリングできる
