Database Auditing in Spring boot with spring security context and spring data JPA

Mayank Yaduvanshi
5 min readJun 10, 2023

Introduction

Auditing in a database involves keeping track of the user who performs certain operations on the data, such as inserting, modifying, or deleting entries, as well as the time those actions were taken. This information can be useful for various purposes, such as security and compliance. In Spring Boot, auditing can be implemented using the @CreatedDate, @CreatedBy, @LastModifiedDate, and @LastModifiedBy annotations, as well as the AuditingEntityListener and AuditorAware interfaces.

More on auditing annotations

@CreatedDate is used to automatically set the creation date of an entity. When a new entity is persisted, the current date and time will be automatically set as the creation date.

@CreatedBy is used to automatically set the username of the user who created an entity. When a new entity is persisted, the username of the currently authenticated user will be automatically set as the creator.

@LastModifiedDate is used to automatically set the last modified date of an entity. Whenever an entity is updated, the current date and time will be automatically set as the last modified date.

@LastModifiedBy annotation is used to automatically update the field that represents the last modifier of an entity when it is modified.

Apart from these there some other annotations also available, and helpful in extending the auditing functionality

@PrePersist is used to mark a method that is executed automatically before an entity is persisted (inserted) into the database.

@PreUpdate is used to mark a method that is automatically executed before an entity is updated (changes are synchronised) with the database.

With the help of @PrePersist and @PreUpdate, we can define custom logic to be executed inside annotated method, such as setting default values or performing any other actions on the entity before it is saved.

In this article we will learn how to put these annotations and interfaces to work and automatically update the auditor details in the database.

Goal

Main goal of this example is to demonstrate how we can update certain columns (related to auditing like createdBy, createdOn, updatedBy, updatedOn) in a table of database by utilising the spring’s auditing feature and security context.

Source code

mayank042/spring-jpa-auditing (github.com)

Pre-requisites

This example is based on assumption that Spring web security is configured in the application, as we will fetch the auditor (signed user) details from the security context. Or you can modify the implementation of AuditAware (see below) not to use the security context but get user details from other source.

You can see the security configuration implemented in the source code itself, will not talk much about that as it is out of the scope of this article.

Below is an basic example of how to set authenticated user details in the spring security context

// set authenticated userDeatils in security context

// userDeatils = instance of org.springframework.security.core.userdetails.UserDetails or can be a implementation of this class (where you can add your own required prop.
var userDetails = ...

var authentication = new UsernamePasswordAuthenticationToken(userDetails, "", authorities);

SecurityContextHolder.getContext().setAuthentication(authentication);

Get your hands dirty

Dependencies

Start by adding the required maven dependencies -

Spring data JPA — is a powerful framework that simplifies working with relational databases in Java applications by providing automated CRUD operations, query generation, and repository support with minimal code.

Spring Security — provides authentication, authorization, and other security features. It simplifies the implementation of user authentication, role-based access control, and protection against common security vulnerabilities.

<!-- pom.xml -->

<!--JPA-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>

<!--Security-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>

Spring AuditorAware

AuditorAware is an interface provided by Spring Data JPA that is used to dynamically determine the current auditor (user) for auditing purposes.

The AuditorAware interface has a single method: getCurrentAuditor(). This method is responsible for providing the current auditor's identification, such as the username or ID. The returned value is then automatically set in the corresponding auditing fields of the audited entities.

By implementing the AuditorAware interface and overriding the getCurrentAuditor() method, we can customize the logic to determine the current auditor based on our application's requirements. For example, we may retrieve the auditor information from the currently logged-in user, from a security context, or from any other source.

In this example we retrieve information from security context.

Create an class implementing the AuditorAware interface and override method getCurrentAuditor to return logged-in user id.

public class AuditorAwareImpl implements AuditorAware<String> {

@Override
public Optional<String> getCurrentAuditor() {

Authentication authentication = SecurityContextHolder.getContext().getAuthentication();

// todo: add checks if authentication is present and user is not null (i.e. signed in).

SignedUser user = (SignedUser) authentication.getPrincipal();

return Optional.of(user.getUserId().toString());
}

}

Register our auditAware implementation so that spring can use it at runtime, we do it by creating a bean of it and enabling JPA auditing.

Create a configuration class annotated with @EnableJpaAuditing

@Configuration
@EnableJpaAuditing(auditorAwareRef = "auditorProvider")
public class Config {

@Bean
public AuditorAware<String> auditorProvider() {
// our implementation of AuditorAware
return new AuditorAwareImpl();
}

}

Note: value of auditorAwareRef is the name of the method annotated with Bean annotation.

We are done with all the configuration part now its time to use it.

As most of the tables in database will have the common auditing columns like createdBy, createdOn, updatedBy, updatedOn its not a best practice to put their related fields in all entity classes, instead of we can do some code reusability and created a common/base entity class with those fields and extend all other entity classes with that base entity class.

Creating an common Auditing entity

@Getter
@Setter
@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
public abstract class AuditorEntity {

@CreatedDate
@Column(name = "CreatedOn")
private LocalDateTime createdOn;

@CreatedBy
@Column(name = "CreatedBy", length = 50)
private String createdBy;


@LastModifiedDate
@Column(name = "UpdatedOn")
private LocalDateTime updatedOn;

@LastModifiedBy
@Column(name = "UpdatedBy", length = 50)
private String updatedBy;

@Column(name = "DeletedOn")
private LocalDateTime deletedOn;

@Column(name = "DeletedBy", length = 50)
private String deletedBy;

@Column(name = "isDeleted", length = 50)
private Boolean isDeleted = false;

@PreUpdate
@PrePersist
public void beforeAnyUpdate() {
if (isDeleted != null && isDeleted) {

if (deletedBy == null) {
deletedBy = SignedUserHelper.userId().toString();
}

if (getDeletedOn() == null) {
deletedOn = LocalDateTime.now();
}
}
}

}

Pay attention on the annotation here @EntityListeners(AuditingEntityListener.class)

When you annotate an entity class with @EntityListeners, you can specify one or more listener classes that implement the corresponding JPA callback methods. These methods will be invoked automatically when the specified events occur on the entity, such as entity creation, update, or deletion.

AuditingEntityListener.class is a built-in entity listener class provided by Spring Data JPA for auditing purposes. It is used in conjunction with auditing annotations (@CreatedBy, @LastModifiedBy, @CreatedDate, @LastModifiedDate) to automatically populate auditing-related fields in entities.

Creating a real entity with auditing

@Entity
@Table(name = "user")
public class User extends AuditorEntity {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "Id", nullable = false)
private Long id;

@Column(name = "Email")
private String email;

@Column(name = "Name")
private String name;

@Column(name = "Phone")
private String phone;
}

This user entity extends the AuditorEntity which make auditing fields available to User entity.

Now whenever you create a new user —

createdBy — gets auto filled by currently logged in user

createdOn — gets auto filled by current date time

And on updating existing user -

updateBy — gets auto filled by currently logged in user

updateOn — gets auto filled by current date time

Keep in mind that this auditing functionality only works when we create or update entities using the JPA repository’s save method (not by writing native queries).

That’s all for now, keep reading the future publications.

--

--

Mayank Yaduvanshi

Full Stack Software Developer, Experienced in Angular, Flutter, Spring boot, NodeJs, MySQL, MongoDB, AWS, Google Cloud, Docker, Git, JIRA.