Перейти к основному содержимому

Поддержка аудита аутентификации Spring Boot

· 4 мин. чтения

1. Обзор

В этой короткой статье мы рассмотрим модуль Spring Boot Actuator и поддержку публикации событий аутентификации и авторизации в сочетании с Spring Security.

2. Зависимости Maven

Во- первых, нам нужно добавить spring-boot-starter-actuator в наш pom.xml:

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
<version>2.2.2.RELEASE</version>
</dependency>

Последняя версия доступна в репозитории 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 будет работать; основы новой поддержки событий Spring довольно просты:

  • аннотировать метод с помощью @EventListener
  • добавить AuditApplicationEvent в качестве единственного аргумента метода

Результат запуска приложения будет выглядеть примерно так:

Principal anonymousUser - AUTHORIZATION_FAILURE
Remote IP address: 0:0:0:0:0:0:0:1
Session Id: null
Principal user - AUTHENTICATION_FAILURE
Remote IP address: 0:0:0:0:0:0:0:1
Session Id: BD41692232875A5A65C5E35E63D784F6
Principal user - AUTHENTICATION_SUCCESS
Remote IP address: 0:0:0:0:0:0:0:1
Session Id: BD41692232875A5A65C5E35E63D784F6

В этом примере прослушиватель получил три события AuditApplicationEvent :

  1. Без входа в систему был запрошен доступ к странице с ограниченным доступом
  2. При входе в систему был использован неправильный пароль
  3. Правильный пароль был использован во второй раз

4. Слушатель аудита аутентификации

Если информации, предоставленной AuthorizationAuditListener Spring Boot, недостаточно, вы можете создать свой собственный 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:

Principal anonymousUser - AUTHORIZATION_FAILURE
Remote IP address: 0:0:0:0:0:0:0:1
Session Id: null
Request URL: /hello

Обратите внимание, что в этом примере мы расширились от абстрактного AbstractAuthorizationAuditListener , поэтому мы можем использовать метод публикации из этого базового класса в нашей реализации.

Если вы хотите протестировать его, проверьте исходный код и запустите:

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 регистрация попыток аутентификации и авторизации пользователей становится тривиальной. Читатель также отсылается к аудиту готовности к производству для получения дополнительной информации.

Код из этой статьи можно найти на GitHub .