Статьи

Весенняя загрузка и события безопасности с приводом

Spring Boot Actuator предоставляет возможности аудита для публикации и прослушивания связанных с безопасностью событий в приложении Spring Boot с включенной Spring Security. События по умолчанию — это успешная аутентификация, неудачная аутентификация и отказ в доступе, но они могут быть расширены с помощью пользовательских событий.

Убедитесь, что в вашем проекте включены Spring Boot Security и Actuator

1
2
3
4
5
6
7
8
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

/auditevents точка Actuator /auditevents

По умолчанию /auditevents точка /auditevents включена, поэтому после запуска приложения (и входа в систему с именем user и паролем, указанным в журнале приложения) вы можете видеть текущие события безопасности:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
{
  "events": [
    {
      "timestamp": "2017-03-14T22:59:58+0000",
      "principal": "user",
      "type": "AUTHENTICATION_FAILURE",
      "data": {
        "details": {
          "remoteAddress": "0:0:0:0:0:0:0:1",
          "sessionId": null
        },
        "type": "org.springframework.security.authentication.BadCredentialsException",
        "message": "Bad credentials"
      }
    },
    {
      "timestamp": "2017-03-14T23:00:07+0000",
      "principal": "user",
      "type": "AUTHENTICATION_SUCCESS",
      "data": {
        "details": {
          "remoteAddress": "0:0:0:0:0:0:0:1",
          "sessionId": null
        }
      }
    }
  ]
}

/auditevents точка /auditevents принимает необязательные параметры запроса:

  • pricipal — главное имя
  • after — дата после события произошла в следующем формате: yyyy-MM-dd'T'HH:mm:ssZ
  • typetype события (например, AUTHORIZATION_FAILURE, AUTHENTICATION_SUCCESS, AUTHENTICATION_FAILURE, AUTHENTICATION_SWITCH)

Пример запроса:

HTTP: // локальный: 8080 / auditevents тип = AUTHORIZATION_FAILURE и после = 2017-03-14T23% 3A14% 3A12% 2B0000 и основной = AnonymousUser?

Реализация конечной точки использует org.springframework.boot.actuate.audit.AuditEventRepository для возврата всех зарегистрированных событий аудита.

  • Настроить /auditevents конечную точку

Вы можете настроить конечную точку с помощью свойств endpoints.auditevents.* . Например, чтобы изменить путь конечной точки событий аудита, просто используйте свойство endpoints.auditevents.path .

Прослушивание событий аудита безопасности с @EventListener

События безопасности представлены org.springframework.boot.actuate.audit.AuditEvent значения org.springframework.boot.actuate.audit.AuditEvent в приводе. Этот объект содержит метку времени, имя пользователя, тип события и данные события.

Самый простой способ получить уведомление о событиях аудита — подписаться на события org.springframework.boot.actuate.audit.listener.AuditApplicationEvent помощью Spring org.springframework.context.event.EventListener :

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
@Component
public class AuditApplicationEventListener {
 
    private static final Logger LOG = LoggerFactory.getLogger(AuditApplicationEventListener.class);
 
    @EventListener
    public void onAuditEvent(AuditApplicationEvent event) {
        AuditEvent actualAuditEvent = event.getAuditEvent();
 
        LOG.info("On audit application event: timestamp: {}, principal: {}, type: {}, data: {}",
            actualAuditEvent.getTimestamp(),
            actualAuditEvent.getPrincipal(),
            actualAuditEvent.getType(),
            actualAuditEvent.getData()
        );
 
    }
}

Пример вывода:

1
2017-03-15 00:44:12.921  INFO 13316 --- [nio-8080-exec-1] p.c.d.s.s.AuditApplicationEventListener  : On audit event: timestamp: Wed Mar 15 00:44:12 CET 2017, principal: user, type: AUTHENTICATION_SUCCESS, data: {details=org.springframework.security.web.authentication.WebAuthenticationDetails@b364: RemoteIpAddress: 0:0:0:0:0:0:0:1; SessionId: null}

Асинхронные события

@EventListener является синхронным, но если требуется асинхронное поведение, вы можете аннотировать метод прослушивателя событий с помощью @Async и убедиться, что асинхронность включена (например, через @EnableAsync ):

01
02
03
04
05
06
07
08
09
10
11
@Component
public class AuditApplicationEventListener {
 
    private static final Logger LOG = LoggerFactory.getLogger(AuditApplicationEventListener.class);
 
    @EventListener
    @Async
    public void onAuditEvent(AuditApplicationEvent event) {
 
    }
}

И конфигурация:

1
2
3
4
5
6
7
8
@SpringBootApplication
@EnableAsync
public class Application {
 
    public static void main(String[] args) {
        SpringApplication.run(Application.class);
    }
}

Прослушивание событий аудита безопасности с AbstractAuditListener

Кроме того, вы можете расширить org.springframework.boot.actuate.audit.listener.AbstractAuditListener и переопределить его org.springframework.boot.actuate.audit.listener.AbstractAuditListener#onAuditEvent :

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
@Component
public class AuditEventListener extends AbstractAuditListener {
 
    private static final Logger LOG = LoggerFactory.getLogger(AuditEventListener.class);
 
    @Override
    protected void onAuditEvent(AuditEvent event) {
        LOG.info("On audit event: timestamp: {}, principal: {}, type: {}, data: {}",
            event.getTimestamp(),
            event.getPrincipal(),
            event.getType(),
            event.getData()
        );
    }
}

Примечание. Никакие события не будут храниться в репозитории событий, поэтому /auditevents точка /auditevents всегда будет возвращать пустой массив. Чтобы это исправить, вы можете внедрить хранилище аудита или расширить его непосредственно из org.springframework.boot.actuate.audit.listener.AuditListener :

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
@Component
public class AuditEventListener extends AbstractAuditListener {
 
    private static final Logger LOG = LoggerFactory.getLogger(AuditEventListener.class);
 
    @Autowired
    private AuditEventRepository auditEventRepository;
 
    @Override
    protected void onAuditEvent(AuditEvent event) {
 
        LOG.info("On audit event: timestamp: {}, principal: {}, type: {}, data: {}",
            event.getTimestamp(),
            event.getPrincipal(),
            event.getType(),
            event.getData()
        );
 
        auditEventRepository.add(event);
    }
}

Публикация собственных аудиторских событий с издателем событий

В приведенном ниже примере издатель событий приложения ( org.springframework.context.ApplicationEventPublisher ) используется для публикации настраиваемого события аудита с типом CUSTOM_AUDIT_EVENT . Новый метод слушателя прослушивает только эти новые события, тогда как предыдущий метод игнорирует их (обратите внимание, что это только пример). Как и любые другие события, пользовательские будут храниться с использованием репозитория событий аудита.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
@Component
public class AuditApplicationEventListener {
 
    private static final Logger LOG = LoggerFactory.getLogger(AuditApplicationEventListener.class);
 
    @Autowired
    private ApplicationEventPublisher applicationEventPublisher;
 
    @EventListener(condition = "#event.auditEvent.type != 'CUSTOM_AUDIT_EVENT'")
    @Async
    public void onAuditEvent(AuditApplicationEvent event) {
        AuditEvent actualAuditEvent = event.getAuditEvent();
 
        LOG.info("On audit application event: timestamp: {}, principal: {}, type: {}, data: {}",
            actualAuditEvent.getTimestamp(),
            actualAuditEvent.getPrincipal(),
            actualAuditEvent.getType(),
            actualAuditEvent.getData()
        );
        applicationEventPublisher.publishEvent(
            new AuditApplicationEvent(
                new AuditEvent(actualAuditEvent.getPrincipal(), "CUSTOM_AUDIT_EVENT")
            )
        );
    }
 
    @EventListener(condition = "#event.auditEvent.type == 'CUSTOM_AUDIT_EVENT'")
    public void onCustomAuditEvent(AuditApplicationEvent event) {
        LOG.info("Handling custom audit event ...");
    }
}

Обратите внимание на пример кода

Пример кода для этой статьи можно найти в репозитории spring-boot-thymeleaf . По умолчанию безопасность отключена в обоих профилях. Включите его, изменив свойство security.basic.enabled в application.properties .