我在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。
仍然不确定发生了什么。
答案 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来拉动。