简体   繁体   中英

Bad Credentials when authenticating with active directory LDAP with Spring Security

I am trying to use Spring Security to perform authentication and authorization in front of my REST controller, which is also secured with SSL. The code I have is mostly derived from the example at https://spring.io/guides/gs/authenticating-ldap/

The application works correctly using the embedded LDAP server as shown in the link. I connect to my home page https://localhost:9000/training/trackerHome and log in with the user "ben" and password "benspassword" (as specified in the link). Everything is fine.

But when I update my properties and try to connect to a real LDAP server, I just keep getting errors that say "Bad Credentials" on the login page. The error log looks like:

training-server_1    | 2020-11-10 17:07:23.728 DEBUG 1 --- [nio-8443-exec-2] o.s.security.web.FilterChainProxy        : /login at position 1 of 14 in additional filter chain; firing Filter: 'WebAsyncManagerIntegrationFilter'
training-server_1    | 2020-11-10 17:07:23.728 DEBUG 1 --- [nio-8443-exec-2] o.s.security.web.FilterChainProxy        : /login at position 2 of 14 in additional filter chain; firing Filter: 'SecurityContextPersistenceFilter'
training-server_1    | 2020-11-10 17:07:23.729 DEBUG 1 --- [nio-8443-exec-2] w.c.HttpSessionSecurityContextRepository : HttpSession returned null object for SPRING_SECURITY_CONTEXT
training-server_1    | 2020-11-10 17:07:23.729 DEBUG 1 --- [nio-8443-exec-2] w.c.HttpSessionSecurityContextRepository : No SecurityContext was available from the HttpSession: org.apache.catalina.session.StandardSessionFacade@1b6d157. A new one will be created.
training-server_1    | 2020-11-10 17:07:23.730 DEBUG 1 --- [nio-8443-exec-2] o.s.security.web.FilterChainProxy        : /login at position 3 of 14 in additional filter chain; firing Filter: 'HeaderWriterFilter'
training-server_1    | 2020-11-10 17:07:23.730 DEBUG 1 --- [nio-8443-exec-2] o.s.security.web.FilterChainProxy        : /login at position 4 of 14 in additional filter chain; firing Filter: 'CsrfFilter'
training-server_1    | 2020-11-10 17:07:23.740 DEBUG 1 --- [nio-8443-exec-2] o.s.security.web.FilterChainProxy        : /login at position 5 of 14 in additional filter chain; firing Filter: 'LogoutFilter'
training-server_1    | 2020-11-10 17:07:23.740 DEBUG 1 --- [nio-8443-exec-2] o.s.s.w.u.matcher.AntPathRequestMatcher  : Checking match of request : '/login'; against '/logout'
training-server_1    | 2020-11-10 17:07:23.741 DEBUG 1 --- [nio-8443-exec-2] o.s.security.web.FilterChainProxy        : /login at position 6 of 14 in additional filter chain; firing Filter: 'UsernamePasswordAuthenticationFilter'
training-server_1    | 2020-11-10 17:07:23.741 DEBUG 1 --- [nio-8443-exec-2] o.s.s.w.u.matcher.AntPathRequestMatcher  : Checking match of request : '/login'; against '/login'
training-server_1    | 2020-11-10 17:07:23.742 DEBUG 1 --- [nio-8443-exec-2] w.a.UsernamePasswordAuthenticationFilter : Request is to process authentication
training-server_1    | 2020-11-10 17:07:23.743 DEBUG 1 --- [nio-8443-exec-2] o.s.s.authentication.ProviderManager     : Authentication attempt using org.springframework.security.ldap.authentication.LdapAuthenticationProvider
training-server_1    | 2020-11-10 17:07:23.744 DEBUG 1 --- [nio-8443-exec-2] o.s.s.l.a.LdapAuthenticationProvider     : **Processing authentication request for user: john.doe**
training-server_1    | 2020-11-10 17:07:23.784 DEBUG 1 --- [nio-8443-exec-2] w.a.UsernamePasswordAuthenticationFilter : Authentication request failed: org.springframework.security.authentication.**BadCredentialsException: Bad credentials**
training-server_1    |
training-server_1    | org.springframework.security.authentication.BadCredentialsException: Bad credentials
training-server_1    |  at org.springframework.security.ldap.authentication.LdapAuthenticationProvider.doAuthentication(LdapAuthenticationProvider.java:197) ~[spring-security-ldap-5.1.1.RELEASE.jar!/:5.1.1.RELEASE]
training-server_1    |  at org.springframework.security.ldap.authentication.AbstractLdapAuthenticationProvider.authenticate(AbstractLdapAuthenticationProvider.java:85) ~[spring-security-ldap-5.1.1.RELEASE.jar!/:5.1.1.RELEASE]
training-server_1    |  at org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:174) ~[spring-security-core-5.1.1.RELEASE.jar!/:5.1.1.RELEASE]
training-server_1    |  at org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:199) ~[spring-security-core-5.1.1.RELEASE.jar!/:5.1.1.RELEASE]
training-server_1    |  at org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter.attemptAuthentication(UsernamePasswordAuthenticationFilter.java:94) ~[spring-security-web-5.1.1.RELEASE.jar!/:5.1.1.RELEASE]
training-server_1    |  at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:212) ~[spring-security-web-5.1.1.RELEASE.jar!/:5.1.1.RELEASE]
training-server_1    |  at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) [spring-security-web-5.1.1.RELEASE.jar!/:5.1.1.RELEASE]
training-server_1    |  at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:116) [spring-security-web-5.1.1.RELEASE.jar!/:5.1.1.RELEASE]
training-server_1    |  at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) [spring-security-web-5.1.1.RELEASE.jar!/:5.1.1.RELEASE]
training-server_1    |  at org.springframework.security.web.csrf.CsrfFilter.doFilterInternal(CsrfFilter.java:124) [spring-security-web-5.1.1.RELEASE.jar!/:5.1.1.RELEASE]
training-server_1    |  at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) [spring-web-5.1.2.RELEASE.jar!/:5.1.2.RELEASE]
training-server_1    |  at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) [spring-security-web-5.1.1.RELEASE.jar!/:5.1.1.RELEASE]
training-server_1    |  at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:66) [spring-security-web-5.1.1.RELEASE.jar!/:5.1.1.RELEASE]
training-server_1    |  at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) [spring-web-5.1.2.RELEASE.jar!/:5.1.2.RELEASE]
training-server_1    |  at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) [spring-security-web-5.1.1.RELEASE.jar!/:5.1.1.RELEASE]
training-server_1    |  at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:105) [spring-security-web-5.1.1.RELEASE.jar!/:5.1.1.RELEASE]
training-server_1    |  at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) [spring-security-web-5.1.1.RELEASE.jar!/:5.1.1.RELEASE]
training-server_1    |  at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:56) [spring-security-web-5.1.1.RELEASE.jar!/:5.1.1.RELEASE]
training-server_1    |  at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) [spring-web-5.1.2.RELEASE.jar!/:5.1.2.RELEASE]
training-server_1    |  at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) [spring-security-web-5.1.1.RELEASE.jar!/:5.1.1.RELEASE]
training-server_1    |  at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:215) [spring-security-web-5.1.1.RELEASE.jar!/:5.1.1.RELEASE]
training-server_1    |  at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:178) [spring-security-web-5.1.1.RELEASE.jar!/:5.1.1.RELEASE]
training-server_1    |  at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:357) [spring-web-5.1.2.RELEASE.jar!/:5.1.2.RELEASE]
training-server_1    |  at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:270) [spring-web-5.1.2.RELEASE.jar!/:5.1.2.RELEASE]
training-server_1    |  at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) [tomcat-embed-core-9.0.12.jar!/:9.0.12]
training-server_1    |  at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) [tomcat-embed-core-9.0.12.jar!/:9.0.12]
training-server_1    |  at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:99) [spring-web-5.1.2.RELEASE.jar!/:5.1.2.RELEASE]
training-server_1    |  at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) [spring-web-5.1.2.RELEASE.jar!/:5.1.2.RELEASE]
training-server_1    |  at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) [tomcat-embed-core-9.0.12.jar!/:9.0.12]
training-server_1    |  at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) [tomcat-embed-core-9.0.12.jar!/:9.0.12]
training-server_1    |  at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:92) [spring-web-5.1.2.RELEASE.jar!/:5.1.2.RELEASE]
training-server_1    |  at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) [spring-web-5.1.2.RELEASE.jar!/:5.1.2.RELEASE]
training-server_1    |  at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) [tomcat-embed-core-9.0.12.jar!/:9.0.12]
training-server_1    |  at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) [tomcat-embed-core-9.0.12.jar!/:9.0.12]
training-server_1    |  at org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:93) [spring-web-5.1.2.RELEASE.jar!/:5.1.2.RELEASE]
training-server_1    |  at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) [spring-web-5.1.2.RELEASE.jar!/:5.1.2.RELEASE]
training-server_1    |  at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) [tomcat-embed-core-9.0.12.jar!/:9.0.12]
training-server_1    |  at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) [tomcat-embed-core-9.0.12.jar!/:9.0.12]
training-server_1    |  at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:200) [spring-web-5.1.2.RELEASE.jar!/:5.1.2.RELEASE]
training-server_1    |  at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) [spring-web-5.1.2.RELEASE.jar!/:5.1.2.RELEASE]
training-server_1    |  at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) [tomcat-embed-core-9.0.12.jar!/:9.0.12]
training-server_1    |  at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) [tomcat-embed-core-9.0.12.jar!/:9.0.12]
training-server_1    |  at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:199) [tomcat-embed-core-9.0.12.jar!/:9.0.12]
training-server_1    |  at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96) [tomcat-embed-core-9.0.12.jar!/:9.0.12]
training-server_1    |  at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:490) [tomcat-embed-core-9.0.12.jar!/:9.0.12]
training-server_1    |  at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:139) [tomcat-embed-core-9.0.12.jar!/:9.0.12]
training-server_1    |  at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92) [tomcat-embed-core-9.0.12.jar!/:9.0.12]
training-server_1    |  at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74) [tomcat-embed-core-9.0.12.jar!/:9.0.12]
training-server_1    |  at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343) [tomcat-embed-core-9.0.12.jar!/:9.0.12]
training-server_1    |  at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:408) [tomcat-embed-core-9.0.12.jar!/:9.0.12]
training-server_1    |  at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66) [tomcat-embed-core-9.0.12.jar!/:9.0.12]
training-server_1    |  at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:770) [tomcat-embed-core-9.0.12.jar!/:9.0.12]
training-server_1    |  at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1415) [tomcat-embed-core-9.0.12.jar!/:9.0.12]
training-server_1    |  at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) [tomcat-embed-core-9.0.12.jar!/:9.0.12]
training-server_1    |  at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) [na:1.8.0_212]
training-server_1    |  at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) [na:1.8.0_212]
training-server_1    |  at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) [tomcat-embed-core-9.0.12.jar!/:9.0.12]
training-server_1    |  at java.lang.Thread.run(Thread.java:748) [na:1.8.0_212]
training-server_1    |
training-server_1    | 2020-11-10 17:07:23.785 DEBUG 1 --- [nio-8443-exec-2] w.a.UsernamePasswordAuthenticationFilter : Updated SecurityContextHolder to contain null Authentication
training-server_1    | 2020-11-10 17:07:23.786 DEBUG 1 --- [nio-8443-exec-2] w.a.UsernamePasswordAuthenticationFilter : Delegating to authentication failure handler org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler@63d9b403
training-server_1    | 2020-11-10 17:07:23.786 DEBUG 1 --- [nio-8443-exec-2] .a.SimpleUrlAuthenticationFailureHandler : Redirecting to /login?error
training-server_1    | 2020-11-10 17:07:23.786 DEBUG 1 --- [nio-8443-exec-2] o.s.s.web.DefaultRedirectStrategy        : Redirecting to '/login?error'
training-server_1    | 2020-11-10 17:07:23.787 DEBUG 1 --- [nio-8443-exec-2] w.c.HttpSessionSecurityContextRepository : SecurityContext is empty or contents are anonymous - context will not be stored in HttpSession.
training-server_1    | 2020-11-10 17:07:23.787 DEBUG 1 --- [nio-8443-exec-2] s.s.w.c.SecurityContextPersistenceFilter : SecurityContextHolder now cleared, as request processing completed

Part of my confusion is the handling of the password. I'm not sure what to specify, if anything, for the password encoder? I fear I may be encoding an already encoded password. I have tried all of the possible types of password encoder, and also not using a password encoder at all. Still get the Bad Credentials error.

application.properties when using embedded LDAP server:

ldap.server.url=ldap://localhost:8389/dc=springframework,dc=org
spring.ldap.username=
spring.ldap.password=
ldap.password.encoder=bcrypt
ldap.user.dn.pattern="uid={0},ou=people"
ldap.group.search.base="ou=groups"


# Configuration of Spring's embedded LDAP server (used for development and testing)
spring.ldap.embedded.ldif=classpath:test-server.ldif
spring.ldap.embedded.port=8389
spring.ldap.embedded.base-dn=dc=springframework,dc=org

test-server.ldif excerpt showing definition of user "ben"

dn: uid=ben,ou=people,dc=springframework,dc=org
objectclass: top
objectclass: person
objectclass: organizationalPerson
objectclass: inetOrgPerson
cn: Ben Alex
sn: Alex
uid: ben
userPassword: $2a$10$c6bSeWPhg06xB1lvmaWNNe4NROmZiSpYhlocU/98HNr2MhIOiSt36

application.properties when using actual LDAP server:

ldap.server.url=ldap://125.126.127.128:389/DC=MY-LAB,DC=MY-COMPANY,dc=local
spring.ldap.username=MY-Lab\manager.doe
spring.ldap.password=validpassword
ldap.password.encoder=none
ldap.user.dn.pattern="uid={0},ou=MY-LAB"
ldap.group.search.base="ou=MY-LAB"

Actual LDAP Server structure:

DC=MY-LAB,DC=MY-COMPANY,DC=local[125.126.127.128]
    OU=Domain Controllers
    OU=MY-LAB
        OU=Users
            OU=Outside Users
                CN=Manager Doe
                      (User principal name = manager.doe)
                CN=John Doe
                      (User principal name = john.doe)
@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

  private static final Logger log = LoggerFactory.getLogger(WebSecurityConfig.class);

  @Value("${ldap.server.url}")
  private String ldapServerUrl;

  @Value("${spring.ldap.username}")
  private String ldapManagerDn;

  @Value("${spring.ldap.password}")
  private String ldapManagerPassword;

  @Value("${ldap.password.encoder}")
  private String ldapPasswordEncoder;

  @Value("${ldap.user.dn.pattern}")
  private String ldapUserDnPattern;

  @Value("${ldap.group.search.base}")
  private String ldapGroupSearchBase;

  @Override
  protected void configure(HttpSecurity http) throws Exception {
    http.authorizeRequests().anyRequest().fullyAuthenticated().and().formLogin();
  }


  @Override
  @SuppressWarnings("deprecation")
  public void configure(AuthenticationManagerBuilder auth) throws Exception {

    PasswordEncoder passwordEncoder;
    switch(ldapPasswordEncoder) {
      case "none":
        passwordEncoder = null;
        break;
      case "bcrypt":
        passwordEncoder = new BCryptPasswordEncoder();
        break;
      case "ldap":
        passwordEncoder = new LdapShaPasswordEncoder();
        break;
      case "MD4":
        passwordEncoder = new Md4PasswordEncoder();
        break;
      case "MD5":
        passwordEncoder = new MessageDigestPasswordEncoder("MD5");
        break;
      case "pbkdf2":
        passwordEncoder = new Pbkdf2PasswordEncoder();
        break;
      case "scrypt":
        passwordEncoder = new SCryptPasswordEncoder();
        break;
      case "SHA-1":
        passwordEncoder = new MessageDigestPasswordEncoder("SHA-1");
        break;
      case "SHA-256":
        passwordEncoder = new MessageDigestPasswordEncoder("SHA-256");
        break;
      case "sha256":
        passwordEncoder = new StandardPasswordEncoder();
        break;
      default:
        log.warn("password encoder property not specified. Using default value.");
        passwordEncoder = new BCryptPasswordEncoder();
    }

    if(passwordEncoder != null) {
      auth.ldapAuthentication().userDnPatterns(ldapUserDnPattern).groupSearchBase(ldapGroupSearchBase).contextSource().url(
          ldapServerUrl).managerDn(ldapManagerDn).managerPassword(ldapManagerPassword).and().passwordCompare().passwordEncoder(
              passwordEncoder).passwordAttribute("userPassword");
    } else {
      
auth.ldapAuthentication().userDnPatterns(ldapUserDnPattern).groupSearchBase(ldapGroupSearchBase).contextSource().url(
          ldapServerUrl).managerDn(ldapManagerDn).managerPassword(ldapManagerPassword).and().passwordCompare().passwordAttribute(
              "userPassword");
    }
  }
}

Any ideas what I've done wrong to make the authentication with the real LDAP server always return "Bad Credentials"?

What password encoder in your actual LDAP? You sure that ldap.password.encoder is equal to it. for example, if it is sha1, the value of ldap.password.encoder will be SHA-1. And if you want not to encode your password. please fix it as below. Hope it useful to you.

import org.springframework.security.crypto.password.NoOpPasswordEncoder;

    switch(ldapPasswordEncoder) {
      case "none":
        passwordEncoder = NoOpPasswordEncoder.getInstance();
        break;
     ....
   }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM