18

I'm writing a web application that requires users to login. My company has an Active Directory server that I'd like to make use of for this purpose. However, I'm having trouble using Spring to authenticate the users credentials.

I'm using Spring Security 3.2.2, Spring Ldap 2.0.1 and Java 1.7.

The Web Application starts well, authentication against InMemory-Authentication works well, too, so the rest of my application seems to be configured correctly.

Here is my Config:

@Configuration
@EnableWebSecurity
public class LdapConfig extends WebSecurityConfigurerAdapter {

    @Bean
    public ActiveDirectoryLdapAuthenticationProvider activeDirectoryLdapAuthenticationProvider() {
        val provider = new ActiveDirectoryLdapAuthenticationProvider("my.domain", "ldap://LDAP_ID:389/OU=A_GROUP,DC=domain,DC=tld");
        provider.setConvertSubErrorCodesToExceptions(true);
        provider.setUseAuthenticationRequestCredentials(true);
        provider.setUseAuthenticationRequestCredentials(true);
        return provider;
    }

    @Bean
    public LoggerListener loggerListener() {
        return new LoggerListener();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.authenticationProvider(activeDirectoryLdapAuthenticationProvider());
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // Configuration for Redirects, Login-Page and stuff
    }   
}

When I try to login using MY_USERNAME and MY_PASSWORD I get a Authentication request failed: org.springframework.security.authentication.BadCredentialsException: Bad credentials

Full Stacktrace:

14:59:00,508 DEBUG UsernamePasswordAuthenticationFilter:205 - Request is to process authentication
14:59:00,509 DEBUG ProviderManager:152 - Authentication attempt using org.springframework.security.ldap.authentication.ad.ActiveDirectoryLdapAuthenticationProvider
14:59:00,509 DEBUG ActiveDirectoryLdapAuthenticationProvider:65 - Processing authentication request for user: USERNAME
14:59:00,563 ERROR ActiveDirectoryLdapAuthenticationProvider:133 - Failed to locate directory entry for authenticated user: USERNAME
javax.naming.NameNotFoundException: [LDAP: error code 32 - 0000208D: NameErr: DSID-0310020A, problem 2001 (NO_OBJECT), data 0, best match of:
    'OU=A_GROUP,DC=domain,DC=tld'
    at com.sun.jndi.ldap.LdapCtx.mapErrorCode(Unknown Source)
    at com.sun.jndi.ldap.LdapCtx.processReturnCode(Unknown Source)
    at com.sun.jndi.ldap.LdapCtx.processReturnCode(Unknown Source)
    at com.sun.jndi.ldap.LdapCtx.searchAux(Unknown Source)
    at com.sun.jndi.ldap.LdapCtx.c_search(Unknown Source)
    at com.sun.jndi.ldap.LdapCtx.c_search(Unknown Source)
    at com.sun.jndi.toolkit.ctx.ComponentDirContext.p_search(Unknown Source)
    at com.sun.jndi.toolkit.ctx.PartialCompositeDirContext.search(Unknown Source)
    at javax.naming.directory.InitialDirContext.search(Unknown Source)
    at org.springframework.security.ldap.SpringSecurityLdapTemplate.searchForSingleEntryInternal(SpringSecurityLdapTemplate.java:208)
    at org.springframework.security.ldap.authentication.ad.ActiveDirectoryLdapAuthenticationProvider.searchForUser(ActiveDirectoryLdapAuthenticationProvider.java:285)
    at org.springframework.security.ldap.authentication.ad.ActiveDirectoryLdapAuthenticationProvider.doAuthentication(ActiveDirectoryLdapAuthenticationProvider.java:130)
    at org.springframework.security.ldap.authentication.AbstractLdapAuthenticationProvider.authenticate(AbstractLdapAuthenticationProvider.java:80)
    at org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:156)
    at org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:177)
    at org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter.attemptAuthentication(UsernamePasswordAuthenticationFilter.java:94)
    at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:211)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:110)
    ... a few more

14:59:00,597  WARN LoggerListener:60 - Authentication event AuthenticationFailureBadCredentialsEvent: USERNAME; details: org.springframework.security.web.authentication.WebAuthenticationDetails@0: RemoteIpAddUSERNAME: 0:0:0:0:0:0:0:1; SessionId: 1E9401031886F0155F0ACE881CC50A4B; exception: Bad credentials
14:59:00,597 DEBUG UsernamePasswordAuthenticationFilter:348 - Authentication request failed: org.springframework.security.authentication.BadCredentialsException: Bad credentials
14:59:00,597 DEBUG UsernamePasswordAuthenticationFilter:349 - Updated SecurityContextHolder to contain null Authentication
14:59:00,597 DEBUG UsernamePasswordAuthenticationFilter:350 - Delegating to authentication failure handler org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler@3d876453

When I browse the AD using a Ldap-Explorer and search for (&(objectClass=user)(userPrincipalName=MY_USERNAME)), which Spring does in ActiveDirectoryLdapAuthenticationProvider:searchForUser(...), it returns the correct user.

When entering an invalid Password, Spring returns ActiveDirectoryLdapAuthenticationProvider:200 - Active Directory authentication failed: Supplied password was invalid. This seems to be OK.

Is a part for the configuration missing?

Are there any working examples of how to configure Spring Ldap for an AD using JavaConfig? The official Spring Guide just describes the XML-Way http://docs.spring.io/spring-security/site/docs/3.1.5.RELEASE/reference/ldap.html#ldap-active-directory

Update: Just updated my AuthenticationProvider to the following:

@Bean
public ActiveDirectoryLdapAuthenticationProvider activeDirectoryLdapAuthenticationProvider() {
    val provider = new ActiveDirectoryLdapAuthenticationProvider("company.tld", "ldap://LDAP_URL:389");
    provider.setConvertSubErrorCodesToExceptions(true);
    provider.setUseAuthenticationRequestCredentials(true);

    provider.setAuthoritiesMapper(myAuthoritiesMapper()); // see http://comdynamics.net/blog/544/spring-security-3-integration-with-active-directory-ldap/

    provider.setUseAuthenticationRequestCredentials(true);

    return provider;
}

It works fine, thanks guido!

Note: Spring states, that a PartialResultException is ignored. The Docs say

Some Active Directory (AD) servers are unable to automatically following referrals, which often leads to a PartialResultException being thrown in searches. You can specify that PartialResultException is to be ignored by setting the ignorePartialResultException property to true.

Maybe there is a way to set this property vie JavaConfig, too. I just ignored it.

fresschen
  • 191
  • 1
  • 1
  • 5
  • 2
    can you try removing the `OU=A_GROUP,DC=domain,DC=tld` base from your connection url? it seems the AD spring provider manage that internally – guido Apr 25 '14 at 14:27
  • Just tried to remove this part and it kinda works. Console logs a `Ignoring PartialResultException` and HTTP 403 Error. – fresschen Apr 25 '14 at 14:34
  • The login-name is the value of `sAMAccountName`. May this be a problem as well? – fresschen Apr 25 '14 at 14:38
  • Standing at the docs, it seems you can login with the username of the full userPrincipalName (username@domain.tld). Pls update your question with your new findings. – guido Apr 25 '14 at 15:05
  • 2
    the link http://comdynamics.net/blog/544/spring-security-3-integration-with-active-directory-ldap is outdated... care to add few more points about this myAuthoritiesMapper()? – Karthik Karuppannan Mar 02 '15 at 21:59
  • I am facing the same problem. User 'username' not found in directory. Incorrect result size: expected 1, actual 0 . but with same user and invalid password. I get proper error message. I have set a userDetailsContextMapper but not authoritiesMapper. and the link comdynamics is out dated. can you please provide more info on how you solved the issue. – Mukun Dec 21 '15 at 07:54
  • Using the proper authoritiesMapper solved this for me. I just used this code, because access rights did just depend on one AD group. adProvider.setAuthoritiesMapper(new GrantedAuthoritiesMapper() { @Override public Collection extends GrantedAuthority> mapAuthorities(final Collection extends GrantedAuthority> authorities) { for (GrantedAuthority authority : authorities) { if ("management".equals(authority.getAuthority())) { return Collections.singletonList(new SimpleGrantedAuthority(Role.ADMIN)); } } return null; } }); – Horst Krause Mar 08 '20 at 12:54

1 Answers1

2
  1. For PartialResultException You should set parameter referral to "follow" on Your context source.

Eg: https://stackoverflow.com/a/26872236/2718510

Community
  • 1
  • 1
Maciej Czarnecki
  • 158
  • 2
  • 10