Prise en charge de l’audit de l’authentification au démarrage du printemps

Prise en charge de l'audit de l'authentification d'amorçage Spring

1. Vue d'ensemble

Dans ce court article, nous allons explorer le module Spring Boot Actuator et la prise en charge de la publication d'événements d'authentification et d'autorisation conjointement avec Spring Security.

2. Dépendances Maven

Tout d'abord, nous devons ajouter lesspring-boot-starter-actuator à nospom.xml:


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

La dernière version est disponible dans le référentielMaven Central.

3. Écoute des événements d'authentification et d'autorisation

Pour consigner toutes les tentatives d'authentification et d'autorisation dans une application Spring Boot, il suffit de définir un bean avec une méthode d'écoute:

@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());
    }
}

Notez que nous ne faisons que générer certaines des informations disponibles dansAuditApplicationEvent pour afficher les informations disponibles. Dans une application réelle, vous souhaiterez peut-être stocker ces informations dans un référentiel ou un cache pour les traiter ultérieurement.

Notez que tout haricot printanier fonctionnera; les bases du nouveau support de l'événement de printemps sont assez simples:

  • annoter la méthode avec@EventListener

  • ajoutez lesAuditApplicationEvent comme seul argument de la méthode

Le résultat de l'exécution de l'application ressemblera à ceci:

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

Dans cet exemple, troisAuditApplicationEvents ont été reçus par l'auditeur:

  1. Sans connexion, l'accès à une page restreinte a été demandé

  2. Un mot de passe incorrect a été utilisé lors de la connexion

  3. Un mot de passe correct a été utilisé la deuxième fois

4. Un écouteur d'audit d'authentification

Si les informations exposées par lesAuthorizationAuditListener de Spring Boot ne suffisent pas, vous pouvezcreate your own bean to expose more information.

Jetons un coup d'œil à un exemple, dans lequel nous exposons également l'URL de la requête qui a été consultée lorsque l'autorisation échoue:

@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));
    }
}

Nous pouvons maintenant enregistrer l'URL de la requête dans notre listener:

@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"));
    }
}

En conséquence, la sortie contient maintenant l'URL demandée:

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

Notez que nous avons étendu le résuméAbstractAuthorizationAuditListener dans cet exemple, nous pouvons donc utiliser la méthodepublish de cette classe de base dans notre implémentation.

Si vous voulez le tester, consultez le code source et exécutez:

mvn clean spring-boot:run

Par la suite, vous pouvez pointer votre navigateur vershttp://localhost:8080/.

5. Stockage des événements d'audit

Par défaut, Spring Boot stocke les événements d'audit dans unAuditEventRepository. Si vous ne créez pas de bean avec une implémentation propre, unInMemoryAuditEventRepository sera câblé pour vous.

LeInMemoryAuditEventRepository est une sorte de tampon circulaire qui stocke les 4000 derniers événements d'audit en mémoire. Ces événements sont ensuite accessibles via le point de terminaison de gestionhttp://localhost:8080/auditevents.

Cela renvoie une représentation JSON des événements d'audit:

{
  "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. Conclusion

Avec la prise en charge de l'actionneur dans Spring Boot, il devient simple de consigner les tentatives d'authentification et d'autorisation des utilisateurs. Le lecteur est également appeléproduction ready auditing pour des informations supplémentaires.

Le code de cet article peut être trouvéover on GitHub.