2008-09-17 9 views
27

मैं एक स्प्रिंग वेब एप्लिकेशन लिख रहा हूं जिसके लिए उपयोगकर्ताओं को लॉगिन करना आवश्यक है। मेरी कंपनी के पास एक सक्रिय निर्देशिका सर्वर है जिसे मैं इस उद्देश्य के लिए उपयोग करना चाहता हूं। हालांकि, मुझे सर्वर से कनेक्ट करने के लिए स्प्रिंग सुरक्षा का उपयोग करने में परेशानी हो रही है।स्प्रिंग सुरक्षा का उपयोग कर सक्रिय निर्देशिका सर्वर के विरुद्ध आप कैसे प्रमाणीकृत करते हैं?

मैं जावा 1.6 के साथ वसंत 2.5.5 और वसंत सुरक्षा 2.0.3 का उपयोग कर रहा हूं।

यदि मैं गलत आईपी पते को LDAP URL बदल, यह एक अपवाद है या कुछ भी फेंक नहीं है, इसलिए यदि यह भी है सर्वर के साथ शुरू करने के लिए करने के लिए कनेक्ट करने के लिए कोशिश कर रहा मैं सोच रहा हूँ।

हालांकि वेब एप्लिकेशन ठीक से शुरू होता है, लेकिन लॉगिन पृष्ठ में दर्ज की गई कोई भी जानकारी अस्वीकार कर दी जाती है। मैंने पहले एक InMemoryDaoImpl का उपयोग किया था, जो ठीक काम करता था, इसलिए मेरा बाकी का एप्लीकेशन सही तरीके से कॉन्फ़िगर किया गया प्रतीत होता है।

यहाँ मेरी सुरक्षा से संबंधित सेम हैं:

<beans:bean id="ldapAuthProvider" class="org.springframework.security.providers.ldap.LdapAuthenticationProvider"> 
    <beans:constructor-arg> 
     <beans:bean class="org.springframework.security.providers.ldap.authenticator.BindAuthenticator"> 
     <beans:constructor-arg ref="initialDirContextFactory" /> 
     <beans:property name="userDnPatterns"> 
      <beans:list> 
      <beans:value>CN={0},OU=SBSUsers,OU=Users,OU=MyBusiness,DC=Acme,DC=com</beans:value> 
      </beans:list> 
     </beans:property> 
     </beans:bean> 
    </beans:constructor-arg> 
    </beans:bean> 

    <beans:bean id="userDetailsService" class="org.springframework.security.userdetails.ldap.LdapUserDetailsManager"> 
    <beans:constructor-arg ref="initialDirContextFactory" /> 
    </beans:bean> 

    <beans:bean id="initialDirContextFactory" class="org.springframework.security.ldap.DefaultInitialDirContextFactory"> 
    <beans:constructor-arg value="ldap://192.168.123.456:389/DC=Acme,DC=com" /> 
    </beans:bean> 
+0

यह वास्तव में एक स्पष्ट प्रश्न के रूप में इतना उत्तर नहीं है - क्या आपके पास वसंत सुरक्षा पैकेज के लिए लॉगिंग चालू है? –

+0

मैंने सब कुछ के लिए लॉगिंग चालू कर दी है। लॉग इन होने वाले किसी भी संदेश को न देखें ... मैंने अपने प्रश्न 4 लॉग कॉन्फ़िगरेशन के साथ अपना प्रश्न अपडेट किया है। – Michael

+0

मुझे इसके लिए आवश्यक जार फ़ाइलों को बताएं। कृपया – addy

उत्तर

35

मैं तुमने किया ही पीटने-मेरी-सिर के खिलाफ-दीवार अनुभव नहीं था, और एक कस्टम प्रमाणीकरण प्रदाता है कि एक LDAP करता लेखन समाप्त हो गया सक्रिय निर्देशिका सर्वर के खिलाफ क्वेरी।

तो मेरी सुरक्षा से संबंधित सेम हैं:

<beans:bean id="contextSource" 
    class="org.springframework.security.ldap.DefaultSpringSecurityContextSource"> 
    <beans:constructor-arg value="ldap://hostname.queso.com:389/" /> 
</beans:bean> 

<beans:bean id="ldapAuthenticationProvider" 
    class="org.queso.ad.service.authentication.LdapAuthenticationProvider"> 
    <beans:property name="authenticator" ref="ldapAuthenticator" /> 
    <custom-authentication-provider /> 
</beans:bean> 

<beans:bean id="ldapAuthenticator" 
    class="org.queso.ad.service.authentication.LdapAuthenticatorImpl"> 
    <beans:property name="contextFactory" ref="contextSource" /> 
    <beans:property name="principalPrefix" value="QUESO\" /> 
</beans:bean> 

फिर LdapAuthenticationProvider वर्ग:

/** 
* Custom Spring Security authentication provider which tries to bind to an LDAP server with 
* the passed-in credentials; of note, when used with the custom {@link LdapAuthenticatorImpl}, 
* does <strong>not</strong> require an LDAP username and password for initial binding. 
* 
* @author Jason 
*/ 
public class LdapAuthenticationProvider implements AuthenticationProvider { 

    private LdapAuthenticator authenticator; 

    public Authentication authenticate(Authentication auth) throws AuthenticationException { 

     // Authenticate, using the passed-in credentials. 
     DirContextOperations authAdapter = authenticator.authenticate(auth); 

     // Creating an LdapAuthenticationToken (rather than using the existing Authentication 
     // object) allows us to add the already-created LDAP context for our app to use later. 
     LdapAuthenticationToken ldapAuth = new LdapAuthenticationToken(auth, "ROLE_USER"); 
     InitialLdapContext ldapContext = (InitialLdapContext) authAdapter 
       .getObjectAttribute("ldapContext"); 
     if (ldapContext != null) { 
      ldapAuth.setContext(ldapContext); 
     } 

     return ldapAuth; 
    } 

    public boolean supports(Class clazz) { 
     return (UsernamePasswordAuthenticationToken.class.isAssignableFrom(clazz)); 
    } 

    public LdapAuthenticator getAuthenticator() { 
     return authenticator; 
    } 

    public void setAuthenticator(LdapAuthenticator authenticator) { 
     this.authenticator = authenticator; 
    } 

} 

फिर LdapAuthenticatorImpl वर्ग:

/** 
* Custom Spring Security LDAP authenticator which tries to bind to an LDAP server using the 
* passed-in credentials; does <strong>not</strong> require "master" credentials for an 
* initial bind prior to searching for the passed-in username. 
* 
* @author Jason 
*/ 
public class LdapAuthenticatorImpl implements LdapAuthenticator { 

    private DefaultSpringSecurityContextSource contextFactory; 
    private String principalPrefix = ""; 

    public DirContextOperations authenticate(Authentication authentication) { 

     // Grab the username and password out of the authentication object. 
     String principal = principalPrefix + authentication.getName(); 
     String password = ""; 
     if (authentication.getCredentials() != null) { 
      password = authentication.getCredentials().toString(); 
     } 

     // If we have a valid username and password, try to authenticate. 
     if (!("".equals(principal.trim())) && !("".equals(password.trim()))) { 
      InitialLdapContext ldapContext = (InitialLdapContext) contextFactory 
        .getReadWriteContext(principal, password); 

      // We need to pass the context back out, so that the auth provider can add it to the 
      // Authentication object. 
      DirContextOperations authAdapter = new DirContextAdapter(); 
      authAdapter.addAttributeValue("ldapContext", ldapContext); 

      return authAdapter; 
     } else { 
      throw new BadCredentialsException("Blank username and/or password!"); 
     } 
    } 

    /** 
    * Since the InitialLdapContext that's stored as a property of an LdapAuthenticationToken is 
    * transient (because it isn't Serializable), we need some way to recreate the 
    * InitialLdapContext if it's null (e.g., if the LdapAuthenticationToken has been serialized 
    * and deserialized). This is that mechanism. 
    * 
    * @param authenticator 
    *   the LdapAuthenticator instance from your application's context 
    * @param auth 
    *   the LdapAuthenticationToken in which to recreate the InitialLdapContext 
    * @return 
    */ 
    static public InitialLdapContext recreateLdapContext(LdapAuthenticator authenticator, 
      LdapAuthenticationToken auth) { 
     DirContextOperations authAdapter = authenticator.authenticate(auth); 
     InitialLdapContext context = (InitialLdapContext) authAdapter 
       .getObjectAttribute("ldapContext"); 
     auth.setContext(context); 
     return context; 
    } 

    public DefaultSpringSecurityContextSource getContextFactory() { 
     return contextFactory; 
    } 

    /** 
    * Set the context factory to use for generating a new LDAP context. 
    * 
    * @param contextFactory 
    */ 
    public void setContextFactory(DefaultSpringSecurityContextSource contextFactory) { 
     this.contextFactory = contextFactory; 
    } 

    public String getPrincipalPrefix() { 
     return principalPrefix; 
    } 

    /** 
    * Set the string to be prepended to all principal names prior to attempting authentication 
    * against the LDAP server. (For example, if the Active Directory wants the domain-name-plus 
    * backslash prepended, use this.) 
    * 
    * @param principalPrefix 
    */ 
    public void setPrincipalPrefix(String principalPrefix) { 
     if (principalPrefix != null) { 
      this.principalPrefix = principalPrefix; 
     } else { 
      this.principalPrefix = ""; 
     } 
    } 

} 

और अंत में, LdapAuthenticationToken वर्ग:

/** 
* <p> 
* Authentication token to use when an app needs further access to the LDAP context used to 
* authenticate the user. 
* </p> 
* 
* <p> 
* When this is the Authentication object stored in the Spring Security context, an application 
* can retrieve the current LDAP context thusly: 
* </p> 
* 
* <pre> 
* LdapAuthenticationToken ldapAuth = (LdapAuthenticationToken) SecurityContextHolder 
*  .getContext().getAuthentication(); 
* InitialLdapContext ldapContext = ldapAuth.getContext(); 
* </pre> 
* 
* @author Jason 
* 
*/ 
public class LdapAuthenticationToken extends AbstractAuthenticationToken { 

    private static final long serialVersionUID = -5040340622950665401L; 

    private Authentication auth; 
    transient private InitialLdapContext context; 
    private List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>(); 

    /** 
    * Construct a new LdapAuthenticationToken, using an existing Authentication object and 
    * granting all users a default authority. 
    * 
    * @param auth 
    * @param defaultAuthority 
    */ 
    public LdapAuthenticationToken(Authentication auth, GrantedAuthority defaultAuthority) { 
     this.auth = auth; 
     if (auth.getAuthorities() != null) { 
      this.authorities.addAll(Arrays.asList(auth.getAuthorities())); 
     } 
     if (defaultAuthority != null) { 
      this.authorities.add(defaultAuthority); 
     } 
     super.setAuthenticated(true); 
    } 

    /** 
    * Construct a new LdapAuthenticationToken, using an existing Authentication object and 
    * granting all users a default authority. 
    * 
    * @param auth 
    * @param defaultAuthority 
    */ 
    public LdapAuthenticationToken(Authentication auth, String defaultAuthority) { 
     this(auth, new GrantedAuthorityImpl(defaultAuthority)); 
    } 

    public GrantedAuthority[] getAuthorities() { 
     GrantedAuthority[] authoritiesArray = this.authorities.toArray(new GrantedAuthority[0]); 
     return authoritiesArray; 
    } 

    public void addAuthority(GrantedAuthority authority) { 
     this.authorities.add(authority); 
    } 

    public Object getCredentials() { 
     return auth.getCredentials(); 
    } 

    public Object getPrincipal() { 
     return auth.getPrincipal(); 
    } 

    /** 
    * Retrieve the LDAP context attached to this user's authentication object. 
    * 
    * @return the LDAP context 
    */ 
    public InitialLdapContext getContext() { 
     return context; 
    } 

    /** 
    * Attach an LDAP context to this user's authentication object. 
    * 
    * @param context 
    *   the LDAP context 
    */ 
    public void setContext(InitialLdapContext context) { 
     this.context = context; 
    } 

} 

आप देखेंगे कि वहां कुछ बिट्स हैं जिनकी आपको आवश्यकता नहीं हो सकती है।

उदाहरण के लिए, मेरे ऐप को लॉग इन किए गए उपयोगकर्ता द्वारा आगे के उपयोग के लिए सफलतापूर्वक लॉग-इन एलडीएपी संदर्भ को बनाए रखने की आवश्यकता है - ऐप का उद्देश्य उपयोगकर्ताओं को उनके एडी प्रमाण-पत्रों के माध्यम से लॉग इन करना है और फिर आगे एडी- संबंधित कार्यों। तो इसके कारण, मेरे पास एक कस्टम प्रमाणीकरण टोकन है, LdapAuthenticationToken, जो मैं चारों ओर पास करता हूं (स्प्रिंग के डिफ़ॉल्ट प्रमाणीकरण टोकन के बजाय) जो मुझे एलडीएपी संदर्भ संलग्न करने की अनुमति देता है। LdapAuthenticationProvider.authenticate() में, मैं उस टोकन को बना देता हूं और उसे वापस भेजता हूं; LdapAuthenticatorImpl.authenticate() में, मैं लॉग-इन संदर्भ को रिटर्न ऑब्जेक्ट में संलग्न करता हूं ताकि इसे उपयोगकर्ता के स्प्रिंग प्रमाणीकरण ऑब्जेक्ट में जोड़ा जा सके।

इसके अलावा, LdapAuthenticationProvider.authenticate() में, मैं सभी लॉग-इन उपयोगकर्ताओं को ROLE_USER भूमिका नियुक्त करता हूं - यही कारण है कि मैं अपने इंटरसेप्ट-यूआरएल तत्वों में उस भूमिका के लिए परीक्षण करने देता हूं। आप इस मैच को जो भी भूमिका निभाना चाहते हैं, या सक्रिय निर्देशिका समूहों या जो भी हो, के आधार पर भूमिकाएं असाइन करना चाहेंगे।

अंत में, और इसके लिए एक अनुशासनिक तरीका, जिस तरह से मैंने LdapAuthenticationProvider.authenticate() लागू किया है, वैध उपयोगकर्ताओं के साथ सभी उपयोगकर्ताओं को समान ROLE_USER भूमिका देता है। जाहिर है, उस विधि में, आप उपयोगकर्ता पर आगे के परीक्षण कर सकते हैं (यानी, एक विशिष्ट एडी समूह में उपयोगकर्ता है?) और सभी पर उपयोगकर्ता पहुंच प्रदान करने से पहले कुछ शर्तों के लिए भी परीक्षण करें, या यहां तक ​​कि कुछ शर्त के लिए भी परीक्षण करें।

+0

मुझे इसके लिए आवश्यक जार फ़ाइलों को बताएं। कृप्या। – addy

+0

मुझे एक ही समस्या का सामना करना पड़ा। लेकिन वसंत के नए संस्करणों में उन तरीकों में से कई विधियों को हटा दिया गया है, इसलिए मैं इस तरह की एक ही काम नहीं कर सकता। मैंने यहां अपने प्रश्न पूछा http://stackoverflow.com/questions/32070142/spring-security-configuratiion-to- प्रमाणीकरण-ldap-user। मुझे आश्चर्य होगा कि क्या आप इस @delfuego – moha

2

बस एक अप-टू-डेट स्थिति को यह लाने के लिए दस्तावेज। स्प्रिंग सिक्योरिटी 3.0 में complete package है जो ldap-bind के साथ-साथ क्वेरी और प्रमाणीकरण की तुलना में समर्पित डिफ़ॉल्ट कार्यान्वयन के साथ है।

0

एसएसएल के बिना एलडीएपी प्रमाणीकरण सुरक्षित नहीं है जब कोई एलडीएपी सर्वर में स्थानांतरित हो जाता है तो उपयोगकर्ता प्रमाण-पत्र देख सकता है। मैं प्रमाणीकरण के लिए एलडीएपीएस: \ प्रोटोकॉल का उपयोग करने का सुझाव देता हूं। इसे वसंत भाग पर किसी भी बड़े बदलाव की आवश्यकता नहीं है लेकिन आप प्रमाण पत्र से संबंधित कुछ मुद्दों के साथ भाग सकते हैं। अधिक जानकारी के लिए LDAP Active Directory authentication in Spring with SSL देखें

16

संदर्भ के लिए, वसंत सुरक्षा 3.1 में प्रमाणीकरण प्रदाता specifically for Active Directory है।

+1

+1 में मेरी सहायता कर सकते हैं यह शीर्ष जवाब होना चाहिए। अनुभव से मैं कह सकता हूं कि यदि आप एडी के खिलाफ प्रमाणीकरण के लिए LdapAuthenticationProvider का उपयोग करते हैं तो आप अपने लिए जीवन को बहुत कठिन बना रहे हैं। वसंत के आधुनिक संस्करणों में आप कोड की 5 लाइनों से कम में जो भी चाहते हैं वह कर सकते हैं। ल्यूक ने ऊपर प्रदान किया गया लिंक देखें। मैंने नीचे दिए गए मेरे उत्तर में विवरण भी दिए हैं। – Cookalino

0

से ऊपर ल्यूक के जवाब:

संदर्भ के लिए, वसंत सुरक्षा 3.1 एक प्रमाणीकरण प्रदाता [विशेष रूप से सक्रिय निर्देशिका के लिए] है [1]।

[1]: http://static.springsource.org/spring-security/site/docs/3.1.x/reference/springsecurity-single.html#ldap-active-directory

मैं ऊपर वसंत सुरक्षा 3.1.1 के साथ करने की कोशिश की: ldap से कुछ मामूली परिवर्तन होते हैं - सक्रिय निर्देशिका समूहों उपयोगकर्ता के माध्यम से मूल मामले के रूप में आ के एक सदस्य है ।

पहले एलडीएपी के तहत समूहों को पूंजीकृत किया गया था और "ROLE_" के साथ उपसर्ग किया गया था, जिससे उन्हें किसी प्रोजेक्ट में टेक्स्ट खोज के साथ ढूंढना आसान हो गया था, लेकिन स्पष्ट रूप से यूनिक्स समूह में समस्याएं हो सकती हैं अगर किसी अजीब कारण के लिए केवल 2 अलग-अलग समूह होते मामले से अलग (यानी खाते और लेखा)।

इसके अलावा वाक्यविन्यास को डोमेन नियंत्रक नाम और बंदरगाह के मैन्युअल विनिर्देश की आवश्यकता होती है, जो इसे अनावश्यकता के लिए थोड़ा डरावना बनाता है। निश्चित रूप से वहाँ जावा में डोमेन, की यानी समकक्ष के लिए SRV DNS रिकॉर्ड को खोजते का एक तरीका है (साम्बा 4 howto से):

$ host -t SRV _ldap._tcp.samdom.example.com. 
_ldap._tcp.samdom.example.com has SRV record 0 100 389 samba.samdom.example.com. 

नियमित एक लुक-अप के बाद:

$ host -t A samba.samdom.example.com. 
samba.samdom.example.com has address 10.0.0.1 

(असल में _kerberos एसआरवी रिकॉर्ड भी देखने की आवश्यकता हो सकती है ...)

उपरोक्त Samba4.0rc1 के साथ था, हम सांबा एडीए में सांबा 3.x एलडीएपी पर्यावरण से प्रगतिशील रूप से उन्नयन कर रहे हैं।

0

ऊपर ल्यूक के जवाब में के रूप में:

स्प्रिंग सुरक्षा 3.1 सक्रिय निर्देशिका के लिए एक प्रमाणीकरण प्रदाता विशेष रूप से है।

यहां सक्रिय जानकारी है कि इसे ActiveDirectoryLdapAuthenticationProvider का उपयोग करके आसानी से कैसे किया जा सकता है।

संसाधनों में।ग्रूवी:

ldapAuthProvider1(ActiveDirectoryLdapAuthenticationProvider, 
     "mydomain.com", 
     "ldap://mydomain.com/" 
) 

Config.groovy में:

grails.plugin.springsecurity.providerNames = ['ldapAuthProvider1'] 

यह सब कोड की जरूरत है। आप अन्य सभी grails.plugin.springsecurity.ldap को हटा सकते हैं। * Config.groovy में सेटिंग्स क्योंकि वे इस एडी सेटअप पर लागू नहीं होते हैं।

प्रलेखन के लिए देखें: http://docs.spring.io/spring-security/site/docs/3.1.x/reference/springsecurity-single.html#ldap-active-directory

0

आप स्प्रिंग सुरक्षा 4 उपयोग कर रहे हैं तो आप भी किसी वर्ग

  • SecurityConfig.java
का उपयोग कर एक ही लागू कर सकते हैं
@Configuration 
@EnableWebSecurity 
public class SecurityConfig extends WebSecurityConfigurerAdapter { 


static final Logger LOGGER = LoggerFactory.getLogger(SecurityConfig.class); 

@Autowired 
protected void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { 
    auth.authenticationProvider(activeDirectoryLdapAuthenticationProvider()); 
} 

@Override 
protected void configure(HttpSecurity http) throws Exception { 
    http 
      .authorizeRequests() 
       .antMatchers("/").permitAll() 
       .anyRequest().authenticated(); 
      .and() 
       .formLogin() 
      .and() 
       .logout(); 
} 

@Bean 
public AuthenticationProvider activeDirectoryLdapAuthenticationProvider() { 
    ActiveDirectoryLdapAuthenticationProvider authenticationProvider = 
     new ActiveDirectoryLdapAuthenticationProvider("<domain>", "<url>"); 

    authenticationProvider.setConvertSubErrorCodesToExceptions(true); 
    authenticationProvider.setUseAuthenticationRequestCredentials(true); 

    return authenticationProvider; 
} 
} 
संबंधित मुद्दे

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