Openldap Spring集成:Bad Credentials

时间:2017-09-13 15:13:29

标签: spring ldap

我在OSX上运行openldap(虽然在投入生产时会转向运行IPA的centos)。

简单的弹簧启动应用程序除了单个家庭控制器,弹簧安全性和ldap启动器之外没有任何东西。 Pom依赖关系如下:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.ldap</groupId>
    <artifactId>spring-ldap-core</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-ldap</artifactId>
</dependency>
<dependency>
    <groupId>com.unboundid</groupId>
    <artifactId>unboundid-ldapsdk</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-test</artifactId>
    <scope>test</scope>
</dependency>

除了上面的依赖项,我还有一个配置类,如下所示:

@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

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

    @Override
    public void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.ldapAuthentication()
            .userDnPatterns("uid={0},ou=users")
            .groupSearchBase("ou=groups")
            .contextSource(contextSource())
            .passwordCompare()
            .passwordEncoder(new LdapShaPasswordEncoder())
            .passwordAttribute("userPassword");      
    }

    @Bean
    public DefaultSpringSecurityContextSource contextSource() {
            return  new DefaultSpringSecurityContextSource(Arrays.asList("ldap://localhost:389/"), "dc=m,dc=com");
    }
}

我的LDAP目录非常简单,它具有以下内容:

# m.com
dn: dc=m,dc=com
objectClass: dcObject
objectClass: organization
o: M
dc: m

# users, m.com
dn: ou=users,dc=m,dc=com
objectClass: organizationalUnit
ou: users

# taylorj, users, m.com
dn: uid=taylorj,ou=users,dc=m,dc=com
objectClass: top
objectClass: account
objectClass: posixAccount
cn: Jonathan Taylor
uid: taylorj
uidNumber: 1
gidNumber: 1
homeDirectory: /home/taylorj
userPassword:: e1NTSEF9UVNVNDBOdlh1bkJqNkhGeVUwekVVYXlNb2RidTErekg=

当我运行spring应用程序时,它建立了与LDAP的连接,我可以看到LDAP服务器上的reuests,我可以看到它搜索OK并返回结果然后对userPassword属性执行CMP,但是, LDAP然后返回错误代码5(LDAP_COMPARE_FALSE),在春天我看到以下异常:

2017-09-13 15:52:13.315 DEBUG 92002 --- [nio-8132-exec-3] o.s.s.l.a.LdapAuthenticationProvider     : Processing authentication request for user: taylorj
2017-09-13 15:52:13.317 DEBUG 92002 --- [nio-8132-exec-3] o.a.c.loader.WebappClassLoaderBase       :     findResources(jndi.properties)
2017-09-13 15:52:13.330 DEBUG 92002 --- [nio-8132-exec-3] o.s.l.c.support.AbstractContextSource    : Got Ldap context on server 'ldap://localhost:389/dc=m,dc=com'
2017-09-13 15:52:13.339 DEBUG 92002 --- [nio-8132-exec-3] .s.s.l.a.PasswordComparisonAuthenticator : Performing LDAP compare of password attribute 'userPassword' for user 'uid=taylorj,ou=users'
2017-09-13 15:52:13.340 DEBUG 92002 --- [nio-8132-exec-3] o.s.l.c.support.AbstractContextSource    : Got Ldap context on server 'ldap://localhost:389/dc=m,dc=com'
2017-09-13 15:52:13.342 DEBUG 92002 --- [nio-8132-exec-3] o.s.b.f.s.DefaultListableBeanFactory     : Returning cached instance of singleton bean 'delegatingApplicationListener'
2017-09-13 15:52:13.342 DEBUG 92002 --- [nio-8132-exec-3] w.a.UsernamePasswordAuthenticationFilter : Authentication request failed: org.springframework.security.authentication.BadCredentialsException: Bad credentials

org.springframework.security.authentication.BadCredentialsException: Bad credentials
    at org.springframework.security.ldap.authentication.PasswordComparisonAuthenticator.authenticate(PasswordComparisonAuthenticator.java:114) ~[spring-security-ldap-4.2.3.RELEASE.jar:4.2.3.RELEASE]

我不确定我做错了什么,因为它似乎是某种内部弹簧的事情导致密码比较失败。

有什么想法吗?

更新

我恢复了春天提供的LDIF,并且工作正常。沮丧之后,我运行以下命令来更改spring LDIF提供的用户密码

ldappasswd -x -D&#34; cn = admin,dc = m,dc = com&#34; -W -S&#34; uid = ben,ou = people,dc = m,dc = com&#34;

运行此命令后,我的登录不再有效

看看Spring提供的LDIF:

dn: uid=ben,ou=people,dc=m,dc=com
objectclass: top
objectclass: person
objectclass: organizationalPerson
objectclass: inetOrgPerson
cn: Ben Alex
sn: Alex
uid: ben
userPassword: {SHA}nFCebWjxfaLbHHG1Qk5UU4trbvQ=

似乎是将密码设置为SHA,而当我运行ldappasswd时,它最终会被设置为Salted SHA {SSHA}。据我在文档中看到,spring实现应该同时处理SHA和SSHA。

仍然不确定发生了什么。

1 个答案:

答案 0 :(得分:2)

事实证明,spring-security有一个bug。

org.springframework.security.ldap.authentication。 PasswordComparisonAuthenticator

setPasswordEncoder方法的初始行:

public void setPasswordEncoder(Object passwordEncoder) {
    if (passwordEncoder instanceof PasswordEncoder) {
        this.usePasswordAttrCompare = false;
        setPasswordEncoder((PasswordEncoder) passwordEncoder);
        return;
    }

如果密码编码器的类型为PasswordEncoder,则将usePasswordAttrCompare设置为 false 。这个问题是,当此标志设置为 false 时,它会回退到让LDAP进行密码比较。由于密码存储为SSHA,Spring无法提前知道SHA Salt,因此只发送密码的SHA1版本,显然不匹配。

在authenticate()

else if (isLdapPasswordCompare(user, ldapTemplate, password)) {
        return user;
}

由于LdapShaPasswordEncoder的实现方式,encode函数只接受明文密码,而isPasswordValid同时接受明文密码和散列密码进行比较,因此可以通过提取盐来计算SSHA变量。

我修改了(我将在某些时候正确地执行它)setPasswordEncoder函数,如下所示:

if (passwordEncoder instanceof PasswordEncoder) {
    if(passwordEncoder instanceof org.springframework.security.authentication.encoding.LdapShaPasswordEncoder) {
        this.usePasswordAttrCompare = true;
    } else {
        this.usePasswordAttrCompare = false;
    }
    setPasswordEncoder((PasswordEncoder) passwordEncoder);
    return;
}

如果我们有LdapShaPasswordEncoder,我们将始终回退到处理明文,SHA和SSHA密码的isPasswordValid方法。

重建spring-security并在新jar中重建我的代码后,它运行正常。现在想出一个更好的方法来将它提交给Spring来拉动。