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