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
:
- Без входа в систему был запрошен доступ к странице с ограниченным доступом
- При входе в систему был использован неправильный пароль
- Правильный пароль был использован во второй раз
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 .