Populate created-by and modified-by fields from authenticated context without treating audit metadata as access control.
Spring Data JPA auditing: record the actor at the write boundary
Capture actor identity deliberately
A receipt write records who created or last changed it. Enable JPA auditing, attach the auditing listener to the entity, and provide AuditorAware from the current authentication. Store a stable subject identifier, not a display name that changes next month. The audit field answers who the application recorded; it does not prove a caller was authorized. Method security and tenant predicates remain separate.
Define non-request writers
Scheduled jobs, queue consumers and migration programs may have no HTTP security context. Choose an explicit service actor for each path or reject the write; do not silently invent a human identity. An imperative SecurityContextHolder is thread-local, so worker threads need their own authenticated execution context and transaction. Keep the actor in the same transaction as the row so a rollback removes both changes.
Verify the lifecycle
Create and update a receipt through Spring-managed repository methods and assert the two actor fields in a fresh persistence context. Test an unauthenticated write path and a worker job. Bulk JPQL updates bypass ordinary entity listeners, so use a separate audit design for those operations; bulk-update behavior is its own contract.
Implementation contract
@Configuration
@EnableJpaAuditing
class ReceiptAuditConfiguration {
@Bean AuditorAware<String> currentActor() {
return () -> Optional.ofNullable(
SecurityContextHolder.getContext().getAuthentication())
.filter(actor -> actor.isAuthenticated()
&& !(actor instanceof AnonymousAuthenticationToken))
.map(Authentication::getName);
}
}
@Entity
@EntityListeners(AuditingEntityListener.class)
class ReceiptEntity {
@Id UUID id;
@CreatedBy @Column(nullable = false) String createdBy;
@LastModifiedBy String modifiedBy;
}Cost and verification
Auditing callbacks write extra columns and inspect caller context on entity changes. The overhead is usually small beside SQL, but bulk imports and high-volume updates need a measured design because entity callbacks may not run.
Common Mistakes
- Do not treat an audit field as proof of authorization.
- Do not let a background writer silently record an absent or misleading human actor.
- Do not expect JPQL bulk updates to invoke entity auditing callbacks.
Read next
Spring Data JPA bulk update: the managed entity can still hold the old value, Spring method security: authorization advice runs through the bean proxy, Spring imperative transaction: a worker thread does not inherit it, Spring Data JPA saveAndFlush is a SQL boundary, not a commit, Spring JWT tenant claims: reject missing or malformed ownership before conversion.
