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

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

1. обзор

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

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

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


    org.springframework.boot
    spring-boot-starter-actuator
    2.1.7.RELEASE

Последняя версия доступна в репозиторииMaven Central.

3. Прослушивание событий аутентификации и авторизации

Чтобы регистрировать все попытки аутентификации и авторизации в приложении Spring Boot, мы можем просто определить компонент с помощью метода слушателя:

@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 как единственный аргумент метода

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

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

В этом примере слушателем были получены триAuditApplicationEvents:

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

  2. При входе в систему использовался неверный пароль

  3. Правильный пароль был использован во второй раз

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

Если информации, предоставляемой Spring BootAuthorizationAuditListener, недостаточно, вы можетеcreate your own bean to expose more information.

Давайте посмотрим на пример, где мы также показываем 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 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, поэтому мы можем использовать метод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 становится легко регистрировать попытки аутентификации и авторизации от пользователей. Читателю также предлагается обратиться кproduction ready auditing за дополнительной информацией.

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