How to implement AuditorAware with Spring Data JPA and Spring Security?

前端 未结 4 1575
星月不相逢
星月不相逢 2021-02-01 19:29

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

4条回答
  •  感情败类
    2021-02-01 19:53

    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 grantedAuthorities;
    
        public UserAwareUserDetails(User user) {
            this(user, new ArrayList());
        }
    
        public UserAwareUserDetails(User user, Collection grantedAuthorities) {
            this.user = user;
            this.grantedAuthorities = grantedAuthorities;
        }
    
        @Override
        public Collection 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();
    }
    

提交回复
热议问题