如何从Spring Security中的LDAP获取其他用户属性?

时间:2016-06-09 14:06:47

标签: java spring-security spring-boot spring-ldap spring-security-ldap

我目前正在尝试开发一个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属性?

我真的没有想法,所以任何帮助都会非常感激: - )

2 个答案:

答案 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();
    }
}