我目前正在尝试开发一个Spring Boot应用程序,其目的是管理LDAP目录中的用户条目。
LDAP登录已经有效;查找用户所属的组也是如此。
此外,我还想使用该用户的更多LDAP属性填充Spring Security Principal
对象。我已经阅读了SO上的几篇文章以及官方的Spring文档,但根本无法使其工作。
这是我目前的代码:
班级AuthenticationConfiguration
@Configuration
class AuthenticationConfiguration extends GlobalAuthenticationConfigurerAdapter {
private String ldapUrl = "ldap://127.0.0.1:10389/dc=corp,dc=org";
private String bindUser = "cn=spring,ou=users,dc=corp,dc=org";
private String bindPW = "<password>";
private String groupSearchBase = "ou=groups";
private String groupSearchFilter = "(member={0})";
private String userDnPattern = "uid={0},ou=users";
private Log log = LogFactory.getLog(this.getClass());
@Override
public void init(AuthenticationManagerBuilder auth) throws Exception {
DefaultSpringSecurityContextSource contextSource = new DefaultSpringSecurityContextSource(ldapUrl);
contextSource.setUserDn(bindUser);
contextSource.setPassword(bindPW);
contextSource.afterPropertiesSet();
log.info(contextSource.getReadOnlyContext().getAttributes("uid=testuser,ou=users")); // returns all LDAP attributes from that user
DefaultLdapAuthoritiesPopulator populator = new DefaultLdapAuthoritiesPopulator(contextSource, groupSearchBase);
populator.setGroupSearchFilter(groupSearchFilter);
populator.setSearchSubtree(true);
populator.setIgnorePartialResultException(true);
auth
.ldapAuthentication()
.ldapAuthoritiesPopulator(populator)
.contextSource(contextSource)
.userDetailsContextMapper(userDetailsContextMapper())
.userDnPatterns(userDnPattern)
;
}
@Bean
public UserDetailsContextMapper userDetailsContextMapper() {
return new CustomUserDetailsContextMapper();
}
}
如您所见,我正在使用userDetailsContextMapper()方法返回CustomUserDetailsContextMapper
的实例:
@Configuration
public class CustomUserDetailsContextMapper extends LdapUserDetailsMapper implements UserDetailsContextMapper {
private Log log = LogFactory.getLog(this.getClass());
@Override
public LdapUserDetails mapUserFromContext(DirContextOperations ctx, String username, Collection<? extends GrantedAuthority> authorities) {
LdapUserDetailsImpl details = (LdapUserDetailsImpl) super.mapUserFromContext(ctx, username, authorities);
log.info("DN from ctx: " + ctx.getDn()); // return correct DN
log.info("Attributes size: " + ctx.getAttributes().size()); // always returns 0
return new CustomUserDetails(details);
}
@Override
public void mapUserToContext(UserDetails user, DirContextAdapter ctx) {
// default
}
}
现在,当您查看AuthenticationConfiguration
中的第一个日志语句时,Spring实际上会将整个用户对象打印到stdout,正确显示用户的所有LDAP属性:
{displayname=displayName: Test User, givenname=givenName: Test, objectclass=objectClass: posixAccount, top [...]}
但是,CustomUserDetailsContextMapper
类中的日志语句没有。虽然第一个正确显示登录用户的DN,但第二个只显示0,即ctx似乎不包含当前用户的任何属性。
我还尝试通过ctx.getAttribute("attribute")
,ctx.getStringAttribute("attribute")
或ctx.getObjectAttribute("attribute")
直接查询属性,但无济于事。
如何从mapUserFromContext
方法中访问LDAP属性?
我真的没有想法,所以任何帮助都会非常感激: - )
答案 0 :(得分:2)
解决了它。问题不在于应用程序代码,而是在LDAP配置中。
因为我使用的是默认的BindAuthenticator,所以Spring Security尝试使用登录表单中指定的用户绑定到LDAP。不幸的是,ou = users下的所有用户在LDAP配置中只有search
权限。将search
更改为read
(请参阅第8.2.3节http://www.openldap.org/doc/admin24/access-control.html)可以解决此问题。
请注意,登录/绑定本身仍然成功(因为auth
权限是search
的一个子集),但任何属性检索都失败,因为这需要read
权限
答案 1 :(得分:1)
@mpm示例的预先说明:
希望对某人有帮助。这是我的UserDetails
public class CustomUserDetails implements LdapUserDetails {
private String iin;
private String colvirId;
private LdapUserDetails details;
public CustomUserDetails(LdapUserDetails details) {
this.details = details;
}
public String getIin() {
return iin;
}
public void setIin(String iin) {
this.iin = iin;
}
public String getColvirId() {
return colvirId;
}
public void setColvirId(String colvirId) {
this.colvirId = colvirId;
}
@Override
public String getDn() {
return details.getDn();
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return details.getAuthorities();
}
@Override
public String getPassword() {
return details.getPassword();
}
@Override
public String getUsername() {
return details.getUsername();
}
@Override
public boolean isAccountNonExpired() {
return details.isAccountNonExpired();
}
@Override
public boolean isAccountNonLocked() {
return details.isAccountNonLocked();
}
@Override
public boolean isCredentialsNonExpired() {
return details.isCredentialsNonExpired();
}
@Override
public boolean isEnabled() {
return details.isEnabled();
}
}