Search code examples
spring-bootspring-securityldap

Spring Security 5.7.2 - external LDAP Server - BadCredentialsException is thrown


Use case:

I upgraded Spring Boot to version 2.7.2 and Spring security to version 5.7.2.

I configured external LDAP Server in SecurityConfiguration config class (via LdapPasswordComparisonAuthenticationManagerFactory Class).

Problem:

"BadCredentialsException: Bad credentials" gets thrown during Spring Boot app start.


Solution

  • Solution (worked for me):

    pom.xml

    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-security</artifactId>
      <version>2.7.2</version>
    </dependency>
    <dependency>
      <groupId>org.springframework.security</groupId>
      <artifactId>spring-security-data</artifactId>
    </dependency>
    <dependency>
      <groupId>org.springframework.ldap</groupId>
      <artifactId>spring-ldap-core</artifactId>
    </dependency>
    <dependency>
    <dependency>
      <groupId>org.springframework.data</groupId>
      <artifactId>spring-data-ldap</artifactId>
    </dependency>
      <groupId>org.springframework.security</groupId>
      <artifactId>spring-security-ldap</artifactId>
    </dependency>
    

    SecurityConfiguration.java

    package com.example.app.config;
    
    // imports
    
    @EnableWebSecurity
    @EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
    @Import(SecurityProblemSupport.class)
    public class SecurityConfiguration
    {
    
        private final Logger log = LoggerFactory.getLogger(SecurityConfiguration.class);
    
        private final TokenProvider tokenProvider;
    
        private final CorsFilter corsFilter;
    
        private final LdapConfiguration ldapConfiguration;
    
        private final SecurityProblemSupport securityProblemSupport;
    
        public SecurityConfiguration(
            TokenProvider tokenProvider,
            CorsFilter corsFilter,
            LdapConfiguration ldapConfiguration,
            SecurityProblemSupport securityProblemSupport
        ) {
            this.tokenProvider = tokenProvider;
            this.corsFilter = corsFilter;
            this.ldapConfiguration = ldapConfiguration;
            this.securityProblemSupport = securityProblemSupport;
        }
    
        @Bean
        public WebSecurityCustomizer webSecurityCustomizer() {
            return (web) -> web.ignoring()
                .antMatchers(HttpMethod.OPTIONS, "/**")
    //            ...
                .antMatchers("/content/**");
        }
    
        @Bean
        public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
            http
                .addFilterBefore(corsFilter, UsernamePasswordAuthenticationFilter.class)
                .exceptionHandling()
                .authenticationEntryPoint(securityProblemSupport)
                .and()
                .csrf()
                .disable()
                .headers()
                .frameOptions()
                .disable()
                .and()
                .sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                .authorizeRequests()
    //            .antMatchers(HttpMethod.GET,"...")
    //            .permitAll()
    //            ...
                .and()
                .apply(securityConfigurerAdapter());
    
            return http.build();
        }
    
        @Bean
        public AuthenticationManager authenticationManager(BaseLdapPathContextSource contextSource, LdapAuthoritiesPopulator authorities) throws AuthenticationException {
            LdapPasswordComparisonAuthenticationManagerFactory factory =
                new LdapPasswordComparisonAuthenticationManagerFactory(contextSource, new LdapShaPasswordEncoder());
    
            factory.setUserDnPatterns(ldapConfiguration.getUserDnPatterns());
            factory.setPasswordAttribute(ldapConfiguration.getPasswordAttribute());
            factory.setUserDetailsContextMapper(userDetailsContextMapper());
            factory.setLdapAuthoritiesPopulator(authorities);
            factory.setContextSource(contextSource);
    
            return factory.createAuthenticationManager();
        }
    
        @Bean
        LdapAuthoritiesPopulator ldapAuthoritiesPopulator() {
            String groupSearchBase = ldapConfiguration.getGroupSearchBase();
    
            DefaultLdapAuthoritiesPopulator authorities =
                new NestedLdapAuthoritiesPopulator(contextSource(), groupSearchBase);
            authorities.setGroupSearchFilter(ldapConfiguration.getGroupSearchFilter());
    
            return authorities;
        }
    
        @Bean
        @ConditionalOnMissingBean
        public ContextSource contextSource() {
            LdapContextSource source = new DefaultSpringSecurityContextSource(ldapConfiguration.getUrl());
            source.setUserDn(ldapConfiguration.getManagerDn());
            source.setPassword(ldapConfiguration.getManagerPw());
    
            return source;
        }
    
    
        @Bean
        public UserDetailsContextMapper userDetailsContextMapper() {
            return new LdapUserDetailsMapper()
            {
                @Override
                public UserDetails mapUserFromContext(DirContextOperations ctx, String username, Collection<? extends GrantedAuthority> authorities) {
    //                ...
                }
            };
        }
    
        private JWTConfigurer securityConfigurerAdapter() {
            return new JWTConfigurer(tokenProvider);
        }
    
    }
    
    

    application-integration.yml

    ...
    spring:
      ldap:
        urls: ldaps://external-ldapserver-uri.com:636
        base: ou=ExtranetUser,dc=domain,dc=com
        username: cn=admin,dc=domain,dc=com
        password: <password>
    
    ...
    
    ldap:
      url: ldaps://external-ldapserver-uri.com:636
      urlPath: dc=domain,dc=com
      passwordAttribute: userPassword
      groupSearchFilter: member={0}
      groupSearchBase: ou=ExtranetUser
      userDnPatterns: cn={0},ou=ExtranetUser
      managerDn: cn=admin,dc=domain,dc=com
      managerPw: <password>