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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/37728277/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-11-03 02:44:14  来源:igfitidea点击:

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

javaspring-securityspring-bootspring-ldapspring-security-ldap

提问by Alex

I'm currently trying to develop a Spring Boot application whose purpose it will be to manage user entries in our LDAP directory.

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

LDAP login already works; so does the lookup of groups a user is member of.

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

Additionally, I'd like to populate the Spring Security Principalobject with some more LDAP attributes of that user. I've read several posts on SO and also the official Spring documentation, but simply cannot get this to work.

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

Here is my current code: Class AuthenticationConfiguration

这是我当前的代码:Class 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();
    }

}

As you can see, I'm using the userDetailsContextMapper() method to return an instance of my CustomUserDetailsContextMapper:

如您所见,我使用 userDetailsContextMapper() 方法返回 my 的一个实例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
    }
}

Now, when you look at that first log statement in AuthenticationConfiguration, Spring will actually print the whole user object to stdout, correctly showing all LDAP attributes from the user: {displayname=displayName: Test User, givenname=givenName: Test, objectclass=objectClass: posixAccount, top [...]}

现在,当您查看 中的第一条日志语句时AuthenticationConfiguration,Spring 实际上会将整个用户对象打印到标准输出,正确显示用户的所有 LDAP 属性: {displayname=displayName: Test User, givenname=givenName: Test, objectclass=objectClass: posixAccount, top [...]}

However, the log statements in my CustomUserDetailsContextMapperclass do not. Although the first one correctly displays the DN of the logged in user, the second one just display 0, i.e. ctx does not seem to contain any attributes from the current user.

但是,我CustomUserDetailsContextMapper班级中的日志语句没有。虽然第一个正确显示了登录用户的 DN,但第二个只显示 0,即 ctx 似乎不包含来自当前用户的任何属性。

I also tried directly querying for attributes via ctx.getAttribute("attribute"), ctx.getStringAttribute("attribute")or ctx.getObjectAttribute("attribute"), to no avail.

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

How can I access LDAP attributes from within the mapUserFromContextmethod?

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

I'm really out of ideas, so any help would be greatly appreciated :-)

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

采纳答案by Alex

Solved it. The problem was not in the application code, but in the LDAP configuration.

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

Because I was using the default BindAuthenticator, Spring Security tried to bind to LDAP with the user specified in the login form. Unfortunately, all users under ou=users only had a searchpermission in the LDAP configuration. Changing searchto read(see point 8.2.3 http://www.openldap.org/doc/admin24/access-control.html) solved this issue for me.

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

Note that the login/bind itself was still successful (because the authpermission is a subset of search), but any retrieval of attributes failed, as this required the readpermission.

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

回答by qwert_ukg

In advance for @mpm example:

提前为@mpm 示例:

Hope it will be helpful for somebody. Here is my decorator for UserDetails

希望它会对某人有所帮助。这是我的装饰器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();
    }
}