We use Hibernate/JPA, Spring, Spring Data and Spring Security in our application. I have a standard User
entity which is mapped using JPA. Further, I have a U
The solution is not to fetch the User
record in the AuditorAware
implementation. This triggers the described loop, since a select query triggers a flush (this is the case since Hibernate/JPA wants to write the data to the database to commit the transaction before executing the select), which triggers a call to AuditorAware#getCurrentAuditor
.
The solution is to store the User
record in the UserDetails
provided to Spring Security. Hence I created my own implementation:
public class UserAwareUserDetails implements UserDetails {
private final User user;
private final Collection extends GrantedAuthority> grantedAuthorities;
public UserAwareUserDetails(User user) {
this(user, new ArrayList());
}
public UserAwareUserDetails(User user, Collection extends GrantedAuthority> grantedAuthorities) {
this.user = user;
this.grantedAuthorities = grantedAuthorities;
}
@Override
public Collection extends GrantedAuthority> getAuthorities() {
return grantedAuthorities;
}
@Override
public String getPassword() {
return user.getSaltedPassword();
}
@Override
public String getUsername() {
return user.getUsername();
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
public User getUser() {
return user;
}
}
Further, I changed my UserDetailsService
to load the User
and create UserAwareUserDetails
. Now it is possible to access the User
instance through the SercurityContextHolder
:
@Override
public User getCurrentAuditor() {
return ((UserAwareUserDetails) SecurityContextHolder.getContext().getAuthentication().getPrincipal()).getUser();
}