Auditing mit Spring Data JPA

Unter dem Begriff des Auditing versteht man in der Softwareentwicklung die Protokollierung von sicherheitsrelevanten Daten.

Spring Data JPA bietet einen Weg um über Annotationen Auditing-Informationen wie Benutzernamen und Änderungszeiten automatisch zu JPA Entitäten abzuspeichern.

Dieser Artikel zeigt die Einrichtung und Anwendung von Spring Data JPA Auditing.

Die Basisklasse

Um Auditing durchgänging in der gesamten Modellschicht zu verwenden empfiehlt es sich, die Auditing Informationen bereits in einer JPA Basisklasse anzugeben.

Das folgende Code-Beispiel zeigt die Klasse ValueObject die als Basisklasse für alle JPA Enitäten im Projekt dienen soll:

 
import java.io.Serializable;
 
import javax.persistence.MappedSuperclass;
 
import lombok.Getter;
import lombok.Setter;
 
@Getter
@Setter
@MappedSuperclass
public abstract class ValueObject implements Serializable {
 
    // ... 
}

Wir verwenden die Lombok Annotationen @Getter und @Setter um die Setter und Getter Methoden für ValueObject genieren zu lassen und den Code leserlich zu halten. Des Weiteren ist die Klasse abstract und mit der JPA Annotation @MappedSuperclass annotiert.

@MappedSuperclass kann in JPA Vererbungshierarchien verwendet werden um Basisklassen zu definieren deren Properties wiederum nur in die davon abgeleiteten Klassen eingebettet werden. Somit gibt es für mit @MappedSuperclass annotierten Klassen keine eigenen Datenbanktabellen, sondern die Properties gehen einfach in die Klassen über die davon ableiten.

Serializable wird implementiert um die JPA Entitäten serialisierbar zu halten.

Spring Data JPA Annotationen

Um unsere Basisklasse - und somit auch alle davon abgeleiteten JPA Entitäten - mit Auditing Informationen auszustatten müssen wir Spring Data JPA spezifische Annotationen anwenden.

 
import java.io.Serializable;
import java.util.Date;
 
import javax.persistence.EntityListeners;
import javax.persistence.MappedSuperclass;
 
import org.springframework.data.annotation.CreatedBy;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedBy;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
 
import lombok.Getter;
import lombok.Setter;
 
@Getter
@Setter
@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
public abstract class ValueObject implements Serializable {
 
    @CreatedDate
    private Date createdDate;
 
    @CreatedBy
    private String createdBy;
 
    @LastModifiedDate
    private Date lastModifiedDate;
 
    @LastModifiedBy
    private String lastModifiedBy;
}

Wie im obigen Beispiel zu sehen ist, führen wir zuerst vier neue Properties ein. Jede dieser Properties dient der Erfassung von Auditing Informationen.

Über die JPA Annotation @EntityListeners wird der Spring Data spezifische Listener AuditingEntityListener angegeben. Dieser wird somit für bestimmte Events aus der Persistenzschicht registriert und kann bei INSERT/UPDATE Operationen die jeweiligen Auditing Properties setzen.

Die folgenden Annotation sind alle Teil von Spring Data JPA:

@CreatedDate setzt das Erfassungsdatum der Entität und wird somit beim ersten INSERT auf die zugehörige Tabelle in der Datenbank gesetzt.

@LastModifiedDate setzt das letzte Modifikationsdatum der Entität und wird somit bei jedem UPDATE auf der zugehörigen Tabelle in der Datenbank gesetzt.

@CreatedBy und @LastModifiedBy werden ebenfalls bei INSERT/UPDATE Operationen aufgerufen, setzen aber die Benutzerinformationen - üblicherweise den Usernamen des angemeldeten Benutzers - in die jeweiligen Spalten.

Benutzerinformationen Speichern

Bei @CreatedBy und @LastModifiedBy stellt sich die Frage wie Spring Data auf die Benutzerinformationen zugreifen kann, da unterschiedliche Technologien für den geschützten Zugriff auf die Applikation eingesetzt werden können. In unserem Fall setzen wir auf Spring Security, einem Framework für den Einbau von Sicherheits- und Loginmechanismen in Spring basierten Anwendungen.

Um Spring Data Zugriff auf die aktuellen Benutzerinformationen zu geben, ist ein Spring Bean vom Typ AuditorAware<T> notwendig. Der generische Parameter T kann dabei hier nicht nur Referenzdatentypen wie String oder Integer stehen, sondern auch für JPA Entitäten wie bspw. User selbst stehen.

In unserer ValueObject Basisklasse soll als Benutzerinformation der Benutzername speichert werden, daher wurden die beiden Properties createdBy und lastModifiedBy mit dem Typ String angegeben. Die Benutzerinformation selbst liegt zur Laufzeit im sogenannten SpringContextHolder, einer Klasse aus dem Spring Security Framework. Auf diese Klasse greifen wir in unserer AuditorAware Implementierung zu, um den Benutzernamen des aktuell angemeldeten Users auszulesen:

 
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.AuditorAware;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;
 
@Component
class SpringSecurityAuditorAware implements AuditorAware<String> { 
     
     private static final String EMPTY_AUDITOR = "";
 
     @Override
     public String getCurrentAuditor() {
          Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
          if (authentication == null) {
                return EMPTY_AUDITOR;
          }
 
          Object object = authentication.getPrincipal();
          if (!(object instanceof UserDetails)) {
                return EMPTY_AUDITOR;
          }
 
          return ((UserDetails) object).getUsername();
     }
}

Konfiguration

Um den Spring Data JPA Auditing Mechanismus nun generell für unser Projekt zu aktivieren, muss die Annotation @EnableJpaAuditing in unserer @Configuration Klasse angegeben werden.

 

@Configuration
@EnableJpaAuditing(auditorAwareRef = "springSecurityAuditorAware")
class ApplicationConfiguration {
	// ...
}

Über @EnableJpaAuditing können wir nun die Referenz zu unserer AuditorAware Implementierung angeben. Über die Annotation können noch weitere Aspekte konfiguriert werden. Wir setzen beispielsweise das Attribut modifyOnCreate auf false, da wir nicht wollen dass das Modifikationsdatum gemeinsam mit dem Erzeugungsdatum gesetzt wird.

Zusammenfassung

Dieser Artikel zeigt die Anwendung von Spring Data JPA Auditing zur applikationsübergreifenden Protokollierung von Benutzerinformationen und Änderungszeiten von JPA Entitäten.

KONTAKTIEREN SIE UNS

Sie sind interessiert an einem unverbindlichen Gespräch oder Austausch via E-Mail?