2013-01-08 9 views
11

हम अपने आवेदन में हाइबरनेट/जेपीए, स्प्रिंग, स्प्रिंग डेटा और स्प्रिंग सिक्योरिटी का उपयोग करते हैं। मेरे पास मानक User इकाई है जो जेपीए का उपयोग करके मैप किया गया है। इसके अलावा, मैं एक UserRepositoryस्प्रिंग डेटा जेपीए और स्प्रिंग सिक्योरिटी के साथ ऑडिटरवेयर को कैसे कार्यान्वित करें?

public interface UserRepository extends CrudRepository<User, Long> { 
    List<User> findByUsername(String username); 
} 

जो क्वेरी तरीकों के नामकरण के लिए स्प्रिंग डाटा सम्मेलन इस प्रकार की है। मेरे पास एक इकाई है

@Entity 
public class Foo extends AbstractAuditable<User, Long> { 
    private String name; 
} 

मैं स्प्रिंग डेटा ऑडिटिंग समर्थन का उपयोग करना चाहता हूं।

@Service 
public class AuditorService implements AuditorAware<User> { 

    private UserRepository userRepository; 

    @Override 
    public User getCurrentAuditor() { 
     String username = SecurityContextHolder.getContext().getAuthentication().getName(); 
     List<User> users = userRepository.findByUsername(username); 
     if (users.size() > 0) { 
      return users.get(0); 
     } else { 
      throw new IllegalArgumentException(); 
     } 
    } 

    @Autowired 
    public void setUserService(UserService userService) { 
     this.userService = userService; 
    } 
} 

जब मैं एक विधि

@Transactional 
public void createFoo() { 
    Foo bar = new Foo(); 
    fooRepository.save(foo); 
} 

कहाँ सब कुछ सही ढंग वायर्ड जाता है और FooRepository एक स्प्रिंग डाटा CrudRepository है बनाने के लिए: (। Descripe here के रूप में) इसलिए मैं एक AuditorService इस प्रकार बनाया। StackOverflowError को findByUsername पर कॉल करने के बाद से डेटाबेस को डेटा फ्लश करने के लिए हाइबरनेट को ट्रिगर करने लगता है जो को ट्रिगर करता है जो AuditorService#getCurrentAuditor को कॉल करता है जो फिर से फ्लश को ट्रिगर करता है और इसी तरह।

इस रिकर्सन से कैसे बचें? User इकाई को लोड करने के लिए कोई "कैननिकल तरीका" है? या क्या हाइबरनेट/जेपीए को फ्लश करने से रोकने का कोई तरीका है?

उत्तर

11

समाधान UserAuditorAware कार्यान्वयन में रिकॉर्ड लाने के लिए नहीं है। यह वर्णित लूप को ट्रिगर करता है, क्योंकि एक चुनिंदा क्वेरी फ्लश को ट्रिगर करती है (यह मामला है क्योंकि हाइबरनेट/जेपीए डेटाबेस को डेटा को चुनने से पहले लेनदेन करने के लिए डेटा लिखना चाहता है), जो AuditorAware#getCurrentAuditor पर कॉल ट्रिगर करता है।

समाधान User रिकॉर्ड UserDetails में वसंत सुरक्षा प्रदान करने के लिए संग्रहीत करना है। इसलिए मैं अपने खुद के कार्यान्वयन बनाया:

public class UserAwareUserDetails implements UserDetails { 

    private final User user; 
    private final Collection<? extends GrantedAuthority> grantedAuthorities; 

    public UserAwareUserDetails(User user) { 
     this(user, new ArrayList<GrantedAuthority>()); 
    } 

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

इसके अलावा, मैं अपने UserDetailsService बदल User लोड और UserAwareUserDetails बनाने के लिए। अब यह SercurityContextHolder के माध्यम से User उदाहरण का उपयोग करना संभव है:

@Override 
public User getCurrentAuditor() { 
    return ((UserAwareUserDetails) SecurityContextHolder.getContext().getAuthentication().getPrincipal()).getUser(); 
} 
3

ऐसा लगता है कि आप दो अलग बातें करने के लिए एक उपयोगकर्ता इकाई का उपयोग करें:

  • प्रमाणीकरण
  • लेखा परीक्षा

मुझे लगता है कि लेखा परीक्षा उद्देश्य के लिए एक विशेष ऑडिटेबल यूज़र तैयार करना बेहतर होगा (इसमें मूल उपयोगकर्ता नाम के समान उपयोगकर्ता नाम होगा)। निम्नलिखित मामले पर विचार करें: आप डेटाबेस से कुछ उपयोगकर्ता को हटाना चाहते हैं। यदि आपकी सभी ऑडिट ऑब्जेक्ट उपयोगकर्ता से जुड़ी हैं तो वे एक) ढीले लेखक बी) कोस्केड द्वारा भी हटाया जा सकता है (इस पर निर्भर करता है कि लिंक कैसे लागू किया गया है)। सुनिश्चित नहीं है कि आप इसे चाहते हैं। तो विशेष AuditableUser का उपयोग करके आप होगा:

  • कोई प्रत्यावर्तन सिस्टम से कुछ उपयोगकर्ता को हटाने के लिए
  • क्षमता और इसके बारे में सभी अंकेक्षण की जानकारी की रक्षा
+0

किसी भी उपयोगकर्ता को फिर से क्यों हटाएं? क्यों हटाया या sth के रूप में चिह्नित नहीं है। – aycanadal

3

ईमानदारी से कहूं तो आप वास्तव में नहीं है एक और इकाई की आवश्यकता है। उदाहरण के लिए, मैं इसी तरह की समस्या थी और मैं निम्नलिखित तरीके से इसका समाधान नहीं:

public class SpringSecurityAuditorAware implements AuditorAware<SUser>, ApplicationListener<ContextRefreshedEvent> { 
    private static final Logger LOGGER = getLogger(SpringSecurityAuditorAware.class); 
    @Autowired 
    SUserRepository repository; 
    private SUser systemUser; 

    @Override 
    public SUser getCurrentAuditor() { 
     final Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); 
     SUser principal; 
     if (authentication == null || !authentication.isAuthenticated()) { 
      principal = systemUser; 
     } else { 
      principal = (SUser) authentication.getPrincipal(); 
     } 
     LOGGER.info(String.format("Current auditor is >>> %s", principal)); 
     return principal; 
    } 

    @Override 
    public void onApplicationEvent(final ContextRefreshedEvent event) { 
     if (this.systemUser == null) { 
      LOGGER.info("%s >>> loading system user"); 
      systemUser = this.repository.findOne(QSUser.sUser.credentials.login.eq("SYSTEM")); 
     } 
    } 
} 

कहाँ SUser दोनों वर्ग जो मैं लेखा परीक्षा के लिए और साथ ही सुरक्षा के लिए इस्तेमाल करते हैं। मेरे पास आपके से भिन्न उपयोग केस हो सकता है और मेरा दृष्टिकोण बाद में हटा दिया जाएगा, लेकिन इसे इस तरह हल किया जा सकता है।

3

मैं एक ही मुद्दा मिल गया है और क्या मैं सिर्फ Propagation.REQUIRES_NEW को findByUsername(username) पद्धति पर प्रचार बदल रहा था, मैं शक था कि लेनदेन के साथ एक समस्या थी, तो मैं एक नया लेन-देन का उपयोग करने के बदल गया है और यह मेरे लिए अच्छी तरह से काम किया। मुझे उम्मीद है कि यह मदद कर सकता है।

@Repository 
public interface UserRepository extends JpaRepository<User, String> { 

    @Transactional(propagation = Propagation.REQUIRES_NEW) 
    List<User> findByUsername(String username); 
} 
संबंधित मुद्दे