如何在 Spring 安全性中从 LDAP 获取额外的用户属性?

How to get additional user attributes from LDAP in Spring Security?

我目前正在尝试开发一个 Spring 引导应用程序,其目的是管理我们 LDAP 目录中的用户条目。

LDAP 登录已经可用;查找用户所属的组也是如此。

此外,我想用该用户的更多 LDAP 属性填充 Spring 安全 Principal 对象。我已经阅读了几篇关于 SO 的文章以及官方 Spring 文档,但就是无法让它工作。

这是我当前的代码: ClassAuthenticationConfiguration

@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() 方法 return 我的 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 实际上会将整个用户对象打印到标准输出,正确显示用户的所有 LDAP 属性: {displayname=displayName: Test User, givenname=givenName: Test, objectclass=objectClass: posixAccount, top [...]}

然而,我的 CustomUserDetailsContextMapper class 中的日志语句没有。尽管第一个正确显示登录用户的 DN,第二个只显示 0,即 ctx 似乎不包含当前用户的任何属性。

我也尝试通过 ctx.getAttribute("attribute")ctx.getStringAttribute("attribute")ctx.getObjectAttribute("attribute") 直接查询属性,但无济于事。

如何从 mapUserFromContext 方法中访问 LDAP 属性?

我真的没有想法,所以任何帮助将不胜感激:-)

解决了。问题不在应用程序代码中,而在 LDAP 配置中。

因为我使用的是默认的 BindAuthenticator,Spring 安全尝试使用登录表单中指定的用户绑定到 LDAP。不幸的是,ou=users 下的所有用户在 LDAP 配置中只有 search 权限。将 search 更改为 read(参见第 8.2.3 点 http://www.openldap.org/doc/admin24/access-control.html)为我解决了这个问题。

请注意 login/bind 本身仍然成功(因为 auth 权限是 search 的子集),但是任何属性检索都失败了,因为这需要 read许可。

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