【问题标题】:Bad Credentials when authenticating with active directory LDAP with Spring Security使用 Spring Security 使用活动目录 LDAP 进行身份验证时出现错误凭据
【发布时间】:2020-11-11 10:03:59
【问题描述】:

我正在尝试使用 Spring Security 在我的 REST 控制器前执行身份验证和授权,该控制器也受 SSL 保护。我拥有的代码主要来自https://spring.io/guides/gs/authenticating-ldap/的示例

应用程序使用嵌入式 LDAP 服务器正常工作,如链接所示。我连接到我的主页 https://localhost:9000/training/trackerHome 并使用用户“ben”和密码“benspassword”(如链接中指定)登录。一切都很好。

但是当我更新我的属性并尝试连接到真正的 LDAP 服务器时,我只是不断收到登录页面上显示“凭据错误”的错误消息。错误日志如下所示:

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

我的部分困惑是密码的处理。我不确定要为密码编码器指定什么(如果有的话)?我担心我可能正在编码一个已经编码的密码。我已经尝试了所有可能类型的密码编码器,而且根本没有使用密码编码器。仍然收到 Bad Credentials 错误。

使用嵌入式 LDAP 服务器时的 application.properties:

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 摘录显示用户“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

使用实际 LDAP 服务器时的 application.properties:

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"

实际的 LDAP 服务器结构:

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");
    }
  }
}

任何想法我做错了什么使真实 LDAP 服务器的身份验证总是返回“错误凭据”?

【问题讨论】:

    标签: java spring ldap spring-security-ldap


    【解决方案1】:

    您的实际 LDAP 中的密码编码器是什么?您确定 ldap.password.encoder 等于它。例如,如果是 sha1,则 ldap.password.encoder 的值将是 SHA-1。如果您不想对密码进行编码。请按如下方式修复。希望对你有用。

    import org.springframework.security.crypto.password.NoOpPasswordEncoder;
    
        switch(ldapPasswordEncoder) {
          case "none":
            passwordEncoder = NoOpPasswordEncoder.getInstance();
            break;
         ....
       }
    
    

    【讨论】:

    • 感谢 NoOpPasswordEncoder 的提示!我修复了上面显示的代码。虽然仍然是同样的错误。我不确定 LDAP 服务器使用什么方法,而且我问的人似乎都不知道。我已经尝试了每种编码方法,但仍然不断收到“坏凭证”。指定 managerDn 和 managerPassword 值似乎有效,因为当我将它们留空时,我收到一个关于无法绑定的错误。仍然不确定出了什么问题。
    • 如果你使用openldap,我记得你可以通过ltb-project.org安装一个工具来管理它的密码。有了它,您可以配置密码编码方式。或者通过一个叫ldapadmin的win工具也可以查询。这是另一个话题:)
    • 我找到了一个 guide 。也许对你有帮助。
    猜你喜欢
    • 2019-11-19
    • 2018-07-15
    • 2018-08-09
    • 2012-11-18
    • 2010-09-13
    • 2013-03-06
    • 1970-01-01
    • 2011-04-16
    • 2015-11-22
    相关资源
    最近更新 更多