Spring 3.1 LDAP Authentication Process: "Bad Credentials" msg When Credentials Are Good

Steve picture Steve · Mar 28, 2012 · Viewed 23.4k times · Source

Spring 3.1 Tomcat 6.*

I'm working on making a Spring 3.1 webapp, authenticating with LDAP.

I tested the LDAP credentials (username, password, ldap URL, search pattern ) with a JNDI styled Java program I wrote (quoted below ). That program worked, dumped all of the users attributes, including the password, which seems to be encrypted on the LDAP server.

When I try to login with the same credentials in Spring 3.1 I get the error message "Bad Credentials".

I got this message in the logs:

DEBUG [org.springframework.security.authentication.ProviderManager:authenticate] (ProviderManager.java:152) - Authentication attempt using org.springframework.security.ldap.authentication.LdapAuthenticationProvider
DEBUG [org.springframework.security.ldap.authentication.AbstractLdapAuthenticationProvider:authenticate] (AbstractLdapAuthenticationProvider.java:51) - Processing authentication request for user: John.A.Smith
DEBUG [org.springframework.security.ldap.authentication.BindAuthenticator:bindWithDn] (BindAuthenticator.java:108) - Attempting to bind as uid=John.A.Smith,ou=People,o=acme.com,o=acme.com
DEBUG [org.springframework.security.ldap.DefaultSpringSecurityContextSource$1:setupEnvironment] (DefaultSpringSecurityContextSource.java:76) - Removing pooling flag for user uid=John.A.Smith,ou=People,o=acme.com,o=acme.com
DEBUG [org.springframework.security.ldap.authentication.BindAuthenticator:handleBindException] (BindAuthenticator.java:152) - Failed to bind as uid=John.A.Smith,ou=People,o=acme.gov: org.springframework.ldap.AuthenticationException: [LDAP: error code 32 - No Such Object]; nested exception is javax.naming.AuthenticationException: [LDAP: error code 32 - No Such Object]
DEBUG [org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter:unsuccessfulAuthentication] (AbstractAuthenticationProcessingFilter.java:340) - Authentication request failed: org.springframework.security.authentication.BadCredentialsException: Bad credentials

In my *-security.xml I tried using tags to make a password comparison and encoding happen, but it didn't help. I tried using md4,md5,plaintext,sha,sha-256,{ssha},{sha} to no avail.

   <s:authentication-manager>
        <s:ldap-authentication-provider user-dn-pattern="uid={0},ou=People,o=noaa.gov" >
          <s:password-compare hash="md5">
            <s:password-encoder hash="md5"/>
          </s:password-compare>
        </s:ldap-authentication-provider>
      </s:authentication-manager>

My networking group is a big, slow, bureaucratic org. Is there a way I can tell what encoding they use, if any, without contacting them?

Any ideas of things I could check out?

This is my *-security.xml as of my last attempt and the java LDAP demo I WAS able to connect with

Thanks.

My *-security.xml file:

<beans xmlns="http://www.springframework.org/schema/beans"  
  xmlns:s="http://www.springframework.org/schema/security"  
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
  xsi:schemaLocation="http://www.springframework.org/schema/beans  
    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd  
    http://www.springframework.org/schema/security  
    http://www.springframework.org/schema/security/spring-security-3.1.xsd">  



  <s:http auto-config="true" use-expressions="true">  
    **<s:intercept-url pattern="/welcome*" access="isAuthenticated()" />** 
    <s:form-login login-page="/login" default-target-url="/welcome"  
      authentication-failure-url="/loginfailed" />  
    <s:logout logout-success-url="/logout" />  
  </s:http>  



  <s:ldap-server url = "ldap://ldap-itc.sam.acme.com:636/o=acme.com"/>  

  <s:authentication-manager>
    <s:ldap-authentication-provider user-dn-pattern="uid={0},ou=People,o=noaa.gov" />
  </s:authentication-manager>

</beans>  

Here is the JNDI style LDAP Java program that WORKS with the same credentials:

import javax.naming.*;  
import javax.naming.directory.*;  
import java.util.*;  
import java.sql.*;  

public class LDAPDEMO {  

    public static void main(String args[]) {  

        String lcf                = "com.sun.jndi.ldap.LdapCtxFactory";  
        String ldapurl            = "ldap://ldap-itc.sam.acme.com:636/o=acme.com";  
        String loginid            = "John.A.Smith";  
        String password           = "passowordforjohn";  
        DirContext ctx            = null;  
        Hashtable env             = new Hashtable();  
        Attributes attr           = null;  
        Attributes resultsAttrs   = null;  
        SearchResult result       = null;  
        NamingEnumeration results = null;  
        int iResults              = 0;  


        env.put(Context.INITIAL_CONTEXT_FACTORY, lcf);  
        env.put(Context.PROVIDER_URL, ldapurl);  
        env.put(Context.SECURITY_PROTOCOL, "ssl");  
        env.put(Context.SECURITY_AUTHENTICATION, "simple");  
        env.put(Context.SECURITY_PRINCIPAL, "uid=" + loginid + ",ou=People,o=acme.com");  
        env.put(Context.SECURITY_CREDENTIALS, password);  
        try {  

            ctx     = new InitialDirContext(env);  
            attr    = new BasicAttributes(true);  
            attr.put(new BasicAttribute("uid",loginid));  
            results = ctx.search("ou=People",attr);  

            while (results.hasMore()) {  
                result       = (SearchResult)results.next();  
                resultsAttrs = result.getAttributes();  

                for (NamingEnumeration enumAttributes  = resultsAttrs.getAll(); enumAttributes.hasMore();) {  
                    Attribute a = (Attribute)enumAttributes.next();  
                    System.out.println("attribute: " + a.getID() + " : " + a.get().toString());  


                }// end for loop  

                iResults++;  
            }// end while loop  

            System.out.println("iResults == " + iResults);  

        }// end try  
        catch (Exception e) {  
            e.printStackTrace();  
        }  



    }// end function main()  
}// end class LDAPDEMO  

Solution


This comment from Luke Taylor helped me get my configuration working:

Your configuration is wrong in that you have "o=acme.com" in the LDAP server URL and are also using "o=acme.com" in the user DN pattern.

I took the "o=acme.com" out of the DN pattern and the LDAP worked. I had originally put the "o=acme.com" in both the LDAP URL and the DN pattern because I am new to Spring 3.1 and LDAP, and that is similar to how it is/was done in the Java JNDI version of the LDAP demo I wrote based on the legacy code I am replacing.

Here is the final, working version of my *-security.xml

<beans xmlns="http://www.springframework.org/schema/beans"  
  xmlns:s="http://www.springframework.org/schema/security"  
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
  xsi:schemaLocation="http://www.springframework.org/schema/beans  
    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd  
    http://www.springframework.org/schema/security  
    http://www.springframework.org/schema/security/spring-security-3.1.xsd">  



  <s:http auto-config="true" use-expressions="true">  
    **<s:intercept-url pattern="/welcome*" access="isAuthenticated()" />** 
    <s:form-login login-page="/login" default-target-url="/welcome"  
      authentication-failure-url="/loginfailed" />  
    <s:logout logout-success-url="/logout" />  
  </s:http>  



  <s:ldap-server url = "ldap://ldap-itc.sam.acme.com:636/o=acme.com"/>  

  <s:authentication-manager>
    <s:ldap-authentication-provider user-dn-pattern="uid={0},ou=People" />
  </s:authentication-manager>

</beans>  

I'm going to explore his other comment and see if I can put the password encoding back in or if I need to.

Answer

Shaun the Sheep picture Shaun the Sheep · Apr 4, 2012

Your Java example is using standard bind authentication, but you have set the Spring Security configuration to do an LDAP compare operation on the user's password. This will fail because the LDAP server is not using the same password encoding format as Spring Security's MD5 encoder. For a compare operation to succeed, the stored value must match the string that is sent to the directory. In most cases you want to use standard LDAP (bind) authentication. You'll probably need to use a bean configuration for the authentication provider. Try using:

<s:ldap-server id="contextSource" url="ldap://ldap-itc.sam.acme.com:636/o=acme.com"/>

<bean id="ldapAuthProvider" class="org.springframework.security.ldap.authentication.LdapAuthenticationProvider">
 <constructor-arg>
   <bean class="org.springframework.security.ldap.authentication.BindAuthenticator">
     <constructor-arg ref="contextSource"/>
     <property name="userDnPatterns">
       <list><value>uid={0},ou=People</value></list>
     </property>
   </bean>
 </constructor-arg>
 <constructor-arg>
   <bean class="org.springframework.security.ldap.authentication.NullLdapAuthoritiesPopulator"/>
 </constructor-arg>
  <property name="authoritiesMapper">
    <bean class="class="org.springframework.security.core.authority.mapping">
       <property name="defaultAuthority" value="ROLE_USER" />
    </bean>
  </property>   
</bean>

<s:authentication-manager>
  <s:authentication-manager ref="ldapAuthProvider" />
</s:authentication-manager>

I'd recommend you also read the LDAP chapter of the reference manual.

Also, if you want to know why an authentication is failing, the best place to find out is the log for the LDAP server itself. If you don't have full access, then find out how it is set up and use a local server (such as OpenLDAP) where you have full control.