2010-09-12 6 views
7

मैं हाइबरनेट/जेपीए 2 में स्प्रिंग सुरक्षा डेटाबेस प्रमाणीकरण के साथ काम करने के लिए डीएओ को लागू करने की कोशिश कर रहा हूं। स्प्रिंग क्रम में निम्नलिखित संबंधों और संगठनों का उपयोग करता है उपयोगकर्ता & भूमिकाओं का प्रतिनिधित्व करने के लिए:हाइबरनेट/जेपीए 2 के साथ वसंत सुरक्षा उपयोगकर्ता/प्राधिकरणों को कैसे लागू करें?

alt text

PostgreSQL बनाने क्वेरी के रूप में repesented:

CREATE TABLE users 
(
    username character varying(50) NOT NULL, 
    "password" character varying(50) NOT NULL, 
    enabled boolean NOT NULL, 
    CONSTRAINT users_pkey PRIMARY KEY (username) 
); 
CREATE TABLE authorities 
(
    username character varying(50) NOT NULL, 
    authority character varying(50) NOT NULL, 
    CONSTRAINT fk_authorities_users FOREIGN KEY (username) 
     REFERENCES users (username) MATCH SIMPLE 
     ON UPDATE NO ACTION ON DELETE NO ACTION 
); 

GrantedAuthorities, UserDetailsService और UserDetailsmanager की ऑन-बोर्ड कार्यान्वयन का उपयोग करना, सब कुछ ठीक है। हालांकि, मैं वसंत के जेडीबीसी कार्यान्वयन से संतुष्ट नहीं हूं और अपने स्वयं के लिखना चाहूंगा। ऐसा करने के लिए में, मैं निम्नलिखित व्यापार वस्तुओं से संबंधों का प्रतिनिधित्व बनाने की कोशिश की:

उपयोगकर्ता इकाई:

@Entity 
@Table(name = "users", uniqueConstraints = {@UniqueConstraint(columnNames = {"username"})}) 
public class AppUser implements UserDetails, CredentialsContainer { 

    private static final long serialVersionUID = -8275492272371421013L; 

    @Id 
    @Column(name = "username", nullable = false, unique = true) 
    private String username; 

    @Column(name = "password", nullable = false) 
    @NotNull 
    private String password; 

    @OneToMany(
      fetch = FetchType.EAGER, cascade = CascadeType.ALL, 
      mappedBy = "appUser" 
    ) 
    private Set<AppAuthority> appAuthorities; 

    @Column(name = "accountNonExpired") 
    private Boolean accountNonExpired; 

    @Column(name = "accountNonLocked") 
    private Boolean accountNonLocked; 

    @Column(name = "credentialsNonExpired") 
    private Boolean credentialsNonExpired; 

    @OneToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL) 
    @JoinColumn(name = "personalinformation_fk", nullable = true) 
    @JsonIgnore 
    private PersonalInformation personalInformation; 

    @Column(name = "enabled", nullable = false) 
    @NotNull 
    private Boolean enabled; 

    public AppUser(
      String username, 
      String password, 
      boolean enabled, 
      boolean accountNonExpired, 
      boolean credentialsNonExpired, 
      boolean accountNonLocked, 
      Collection<? extends AppAuthority> authorities, 
      PersonalInformation personalInformation 
    ) { 
     if (((username == null) || "".equals(username)) || (password == null)) { 
      throw new IllegalArgumentException("Cannot pass null or empty values to constructor"); 
     } 

     this.username = username; 
     this.password = password; 
     this.enabled = enabled; 
     this.accountNonExpired = accountNonExpired; 
     this.credentialsNonExpired = credentialsNonExpired; 
     this.accountNonLocked = accountNonLocked; 
     this.appAuthorities = Collections.unmodifiableSet(sortAuthorities(authorities)); 
     this.personalInformation = personalInformation; 
    } 

    public AppUser() { 
    } 

    @JsonIgnore 
    public PersonalInformation getPersonalInformation() { 
     return personalInformation; 
    } 

    @JsonIgnore 
    public void setPersonalInformation(PersonalInformation personalInformation) { 
     this.personalInformation = personalInformation; 
    } 

    // Getters, setters 'n other stuff 

और GrantedAuthorities के एक कार्यान्वयन के रूप में अधिकार इकाई:

@Entity 
@Table(name = "authorities", uniqueConstraints = {@UniqueConstraint(columnNames = {"id"})}) 
public class AppAuthority implements GrantedAuthority, Serializable { 
    //~ Instance fields ================================================================================================ 

    private static final long serialVersionUID = 1L; 

    @Id 
    @GeneratedValue(strategy = GenerationType.TABLE) 
    @Column(name = "id", nullable = false) 
    private Integer id; 

    @Column(name = "username", nullable = false) 
    private String username; 

    @Column(name = "authority", nullable = false) 
    private String authority; 

    // Here comes the buggy attribute. It is supposed to repesent the 
    // association username<->username, but I just don't know how to 
    // implement it 
    @ManyToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL) 
    @JoinColumn(name = "appuser_fk") 
    private AppUser appUser; 

    //~ Constructors =================================================================================================== 

    public AppAuthority(String username, String authority) { 
     Assert.hasText(authority, 
       "A granted authority textual representation is required"); 
     this.username = username; 
     this.authority = authority; 
    } 

    public AppAuthority() { 
    } 

    // Getters 'n setters 'n other stuff 

मेरी समस्या @ManyToOne assoc है। AppAuthorities: इसे "उपयोगकर्ता नाम" माना जाता है, लेकिन कोशिश करने और ऐसा करने से त्रुटि उत्पन्न होती है, क्योंकि मुझे उस विशेषता को String के रूप में टाइप करना होगा ... जबकि हाइबरनेट संबंधित इकाई की अपेक्षा करता है। तो मैंने जो कोशिश की वह वास्तव में सही इकाई प्रदान कर रहा है और @JoinColumn(name = "appuser_fk") द्वारा एसोसिएशन बना रहा है। यह निश्चित रूप से, बकवास है, क्योंकि उपयोगकर्ता को लोड करने के लिए, मेरे पास username में विदेशी कुंजी होगी, जबकि हाइबरनेट appuser_fk में इसकी खोज करेगा, जो हमेशा खाली रहेगा।

तो मेरा प्रश्न यह है: डेटा मॉडल के सही जेपीए 2 कार्यान्वयन के लिए उपरोक्त मेटोन कोड को संशोधित करने के बारे में कोई सुझाव?

धन्यवाद

उत्तर

8

आप AppAuthority पर username की आवश्यकता नहीं है। वसंत सुरक्षा इस पर निर्भर नहीं हो सकती है क्योंकि यह GrantedAuthority interface पर निर्भर करती है जिसमें उपयोगकर्ता नाम तक पहुंचने के लिए कोई तरीका नहीं है।

लेकिन बेहतर अभ्यास स्प्रिंग सुरक्षा से अपने डोमेन मॉडल को कम करना है। जब आपके पास कस्टम UserDetailsService है, तो आपको न तो स्प्रिंग सिक्योरिटी की डिफ़ॉल्ट डेटाबेस स्कीमा और न ही ऑब्जेक्ट मॉडल की नकल करने की आवश्यकता नहीं है। आपका UserDetailsService अपना खुद का AppUser और AppAuthority लोड कर सकता है और उसके बाद UserDetails और GrantedAuthority एस बना सकता है। यह चिंताओं के बेहतर अलगाव के साथ क्लीनर डिजाइन की ओर जाता है।

+0

आह! यह तो बहुत अच्छी खबर है! जैसा कि आप सुझाव देते हैं, मैं ठीक से आगे बढ़ूंगा, धन्यवाद। –

+0

क्या आप प्रोजेक्ट यूआरएल साझा कर सकते हैं? मैं पहले से ही oauth2 के साथ वसंत बूट सुरक्षा आराम एपीआई का उपयोग कर एक मॉड्यूल बनाने के लिए अपने 5 दिनों बर्बाद कर दिया। समस्या तब होती है जब मैं यूआरएल को "अधिकारियों" जैसे झूठे डेटा दे रहा हूं: शून्य, "खाता नॉन एक्स्पेरड": झूठी, "खाता नॉन लॉक": झूठी, "क्रेडेंशियल नॉन एक्स्पेरड": झूठी, "सक्षम": झूठी। – Harsh

0

यह एक डोमेन विशिष्ट कुंजी का उपयोग कर की क्लासिक हाइबरनेट समस्या की तरह लग रहा है। एक नया प्राथमिक कुंजी फ़ील्ड बनाने के लिए एक संभावित फिक्स होगा; जैसे userId intUsers और Authorities इकाइयों/तालिकाओं के लिए, Authorities.userName हटाएं, और Users.userName को एक अद्वितीय माध्यमिक कुंजी में बदलें।

+1

हाय। आपका समाधान मेरा पहला अनुमान था, और यदि यह वसंत के लिए नहीं था, तो मैंने इसका इस्तेमाल किया होगा (असल में, अगर मैंने इस डेटा मॉडल को डिजाइन किया था, तो मैं हमेशा * int * इस्तेमाल करता था)। हालांकि, मुझे डर है कि सभी सम्मेलन-ओवर-कॉन्फ़िगरेशन के साथ, * गुणों को हटाने के लिए वसंत सुरक्षा फ्रेमवर्क के बाकी हिस्सों पर अप्रत्याशित दुष्प्रभाव हो सकते हैं। क्या होगा अगर कुछ वसंत सेक। सेम अधिकारियों पर भरोसा करते हैं। उपयोगकर्ता नाम? निश्चित रूप से, मैं उन्हें फिर से कार्यान्वित कर सकता हूं, लेकिन क्या मेरे डेटा मॉडल को छूने से कोई अन्य समाधान नहीं है? सब कुछ, मैं उम्मीद करता हूं कि एक ओआरएम फ्रेमवर्क किसी दिए गए डेटा मॉडल को अपनाने के लिए, और दूसरी तरफ नहीं ... –

0

जेपीए/हाइबरनेट से UserDetailsService को अस्वीकार करने का एक और तरीका है।

आप अपने उपयोगकर्ता और प्राधिकरण वर्ग मॉडल कर सकते हैं के रूप में आप की तरह है और इस जबकि विन्यास में userDetailsService को परिभाषित करने का उपयोग करें: -

<sec:jdbc-user-service data-source-ref="webDS" 
       id="userDetailsService" 
       users-by-username-query="SELECT USER_NAME,PASSWORD,TRUE FROM CUSTOMER WHERE USER_NAME=?" 
       authorities-by-username-query="SELECT c.USER_NAME,r.ROLE_NAME from CUSTOMER c 
              JOIN CUSTOMER_ROLE cr ON c.CUSTOMER_ID = cr.CUSTOMER_ID 
              JOIN ROLE r ON cr.ROLE_ID = r.ROLE_ID 
              WHERE USER_NAME=?" /> 

इस तरह आप अपने डेटाबेस से उपयोगकर्ता और भूमिकाओं को लाने के लिए ठीक देखते SQL क्वेरी परिभाषित कर सकते हैं। आपको केवल टेबल और कॉलम नाम की देखभाल करने की आवश्यकता है।

संबंधित मुद्दे