Spring Boot認証監査サポート
1概要
この短い記事では、Spring Boot Actuatorモジュールと、Spring Securityと連携した認証および承認イベントの発行のサポートについて説明します。
2 Mavenの依存関係
まず、
spring-boot-starter-actuate
を__pom.xmlに追加する必要があります。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
<version>1.5.2.RELEASE</version>
</dependency>
最新版はhttps://search.maven.org/classic/#search%7Cga%7C1%7Cg%3A%22org.springframework.boot%22%20AND%20a%3A%22spring-boot-starter-で入手できます。アクチュエータ%22[Maven Central]リポジトリ。
3認証および承認イベントの待機
Spring Bootアプリケーションですべての認証と承認の試みを記録するために、リスナーメソッドを使ってBeanを定義するだけです。
@Component
public class LoginAttemptsLogger {
@EventListener
public void auditEventHappened(
AuditApplicationEvent auditApplicationEvent) {
AuditEvent auditEvent = auditApplicationEvent.getAuditEvent();
System.out.println("Principal " + auditEvent.getPrincipal()
+ " - " + auditEvent.getType());
WebAuthenticationDetails details =
(WebAuthenticationDetails) auditEvent.getData().get("details");
System.out.println("Remote IP address: "
+ details.getRemoteAddress());
System.out.println(" Session Id: " + details.getSessionId());
}
}
利用可能な情報を示すために、
AuditApplicationEvent
で利用可能なもののいくつかを出力しているだけです。実際のアプリケーションでは、さらに処理するためにその情報をリポジトリまたはキャッシュに格納することをお勧めします。
どのSpring Beanも機能することに注意してください。新しいSpringイベントサポートの基本は非常に簡単です。
-
メソッドに
@ EventListener
のアノテーションを付ける -
メソッドの唯一の引数として
AuditApplicationEvent
を追加する
アプリケーションの実行結果は次のようになります。
……
プリンシパルanonymousUser – AUTHORIZATION
FAILURE
リモートIPアドレス:0:0:0:0:0:0:0:1
セッションID:null
プリンシパルユーザー – AUTHENTICATION
FAILURE
リモートIPアドレス:0:0:0:0:0:0:0:1
セッションID:BD41692232875A5A65C5E35E63D784F6
プリンシパルユーザー – AUTHENTICATION__SUCCESS
リモートIPアドレス:0:0:0:0:0:0:0:1
セッションID:BD41692232875A5A65C5E35E63D784F6
……
この例では、リスナーは3つの
__AuditApplicationEvent
__を受け取りました。
-
ログオンせずに、制限付きページへのアクセスが要求されました
-
ログオン中に間違ったパスワードが使用されました
-
正しいパスワードが2度目に使用されました
4認証監査リスナー
Spring Bootの
AuthorizationAuditListener
によって公開された情報が十分でない場合は、より多くの情報を公開するために
独自のBeanを作成することができます。
認証が失敗したときにアクセスされたリクエストURLも公開する例を見てみましょう。
@Component
public class ExposeAttemptedPathAuthorizationAuditListener
extends AbstractAuthorizationAuditListener {
public static final String AUTHORIZATION__FAILURE
= "AUTHORIZATION__FAILURE";
@Override
public void onApplicationEvent(AbstractAuthorizationEvent event) {
if (event instanceof AuthorizationFailureEvent) {
onAuthorizationFailureEvent((AuthorizationFailureEvent) event);
}
}
private void onAuthorizationFailureEvent(
AuthorizationFailureEvent event) {
Map<String, Object> data = new HashMap<>();
data.put(
"type", event.getAccessDeniedException().getClass().getName());
data.put("message", event.getAccessDeniedException().getMessage());
data.put(
"requestUrl", ((FilterInvocation)event.getSource()).getRequestUrl() );
if (event.getAuthentication().getDetails() != null) {
data.put("details",
event.getAuthentication().getDetails());
}
publish(new AuditEvent(event.getAuthentication().getName(),
AUTHORIZATION__FAILURE, data));
}
}
これで、リクエストURLをリスナーに記録できます。
@Component
public class LoginAttemptsLogger {
@EventListener
public void auditEventHappened(
AuditApplicationEvent auditApplicationEvent) {
AuditEvent auditEvent = auditApplicationEvent.getAuditEvent();
System.out.println("Principal " + auditEvent.getPrincipal()
+ " - " + auditEvent.getType());
WebAuthenticationDetails details
= (WebAuthenticationDetails) auditEvent.getData().get("details");
System.out.println(" Remote IP address: "
+ details.getRemoteAddress());
System.out.println(" Session Id: " + details.getSessionId());
System.out.println(" Request URL: "
+ auditEvent.getData().get("requestUrl"));
}
}
その結果、出力には要求されたURLが含まれるようになりました。
……
プリンシパルanonymousUser – AUTHORIZATION__FAILURE
リモートIPアドレス:0:0:0:0:0:0:0:1
セッションID:null
リクエストURL:/hello
……
この例では抽象
AbstractAuthorizationAuditListener
から拡張したので、実装ではその基本クラスの
publish
メソッドを使用できます。
テストしたい場合は、ソースコードをチェックアウトして、
mvn clean spring-boot:run
それ以降は、ブラウザで `http://localhost:8080/`にアクセスします。
5監査イベントの保存
デフォルトでは、Spring Bootは監査イベントを
AuditEventRepository
に格納します。独自の実装でBeanを作成しなければ、
InMemoryAuditEventRepository
が自動的に配線されます。
InMemoryAuditEventRepository
は、最新の4000個の監査イベントをメモリに格納する一種の循環バッファです。これらのイベントは管理エンドポイント `http://localhost:8080/auditevents`を介してアクセスできます。
これは監査イベントのJSON表現を返します。
{
"events":[ {
"timestamp": "2017-03-09T19:21:59+0000",
"principal": "anonymousUser",
"type": "AUTHORIZATION__FAILURE",
"data": {
"requestUrl": "/auditevents",
"details": {
"remoteAddress": "0:0:0:0:0:0:0:1",
"sessionId": null
},
"type": "org.springframework.security.access.AccessDeniedException",
"message": "Access is denied"
}
},
{
"timestamp": "2017-03-09T19:22:00+0000",
"principal": "anonymousUser",
"type": "AUTHORIZATION__FAILURE",
"data": {
"requestUrl": "/favicon.ico",
"details": {
"remoteAddress": "0:0:0:0:0:0:0:1",
"sessionId": "18FA15865F80760521BBB736D3036901"
},
"type": "org.springframework.security.access.AccessDeniedException",
"message": "Access is denied"
}
},
{
"timestamp": "2017-03-09T19:22:03+0000",
"principal": "user",
"type": "AUTHENTICATION__SUCCESS",
"data": {
"details": {
"remoteAddress": "0:0:0:0:0:0:0:1",
"sessionId": "18FA15865F80760521BBB736D3036901"
}
}
}
]}
6. 結論
Spring Bootのアクチュエータサポートにより、ユーザーからの認証と承認の試みを記録することは簡単になります。いくつかの追加情報については、読者はhttp://docs.spring.io/spring-boot/docs/current/reference/html/production-ready-auditing.html[production ready auditing]も参照してください。
この記事のコードはhttps://github.com/eugenp/tutorials/tree/master/spring-security-core[over on GitHub]にあります。