【问题标题】:Spring Boot / Spring LDAP Get List of memberof for a UserSpring Boot / Spring LDAP 获取用户的成员列表
【发布时间】:2018-01-14 18:02:40
【问题描述】:

我想通过从如下结构的 LDAP 存储库中查询用户 ID 来获取用户属性列表

dn: uid=E000001 ,ou=People,o=Company,o=Internal
cn: BOB DOLE
statusid: active
memberof: cn=foo_group, cn=Foos, ou=Groups, o=Company,o=Internal
memberof: cn=bar_group, cn=Foos, ou=Groups, o=Company,o=Internal

dn: uid=E000002 ,ou=People,o=Company,o=Internal
cn: MARK TEST
statusid: active
memberof: cn=foo_group, cn=Foos, ou=Groups, o=Company,o=Internal
memberof: cn=bar_group, cn=Foos, ou=Groups, o=Company,o=Internal

例如,我查询用户 ID“E00001”。我想退货

["cn=foo_group, cn=Foos, ou=Groups, o=Company,o=Internal", "cn=bar_group, cn=Foos, ou=Groups, o=Company,o=Internal"

【问题讨论】:

    标签: java spring spring-boot spring-ldap


    【解决方案1】:

    这里有多种检索用户组的方法:

    • 如果您有一个没有嵌套组的简单 LDAP 服务器,通常使用 memberOf 就足够了:

      String userCN = "user1";
      
      //Get the attribute of user's "memberOf"
      ArrayList<?> membersOf = ldapTemplate.search(
              query().where("sAMAccountName").is(userCN),
              (AttributesMapper<ArrayList<?>>) attrs -> Collections.list(attrs.get("memberOf").getAll())
      ).get(0);
      
    • 但如果你有嵌套组,事情就会变得更加复杂:

      /*
       * Get user distinguised name, example: "user" -> "CN=User Name,OU=Groups,OU=Domain Users,DC=company,DC=something,DC=org"
       * This will be used for our query later
       */
      String distinguishedName = ldapTemplate.search(
              query().where("sAMAccountName").is(userCN),
              (AttributesMapper<String>) attrs -> attrs.get("distinguishedName").get().toString()
      ).get(0); //.get(0): we assume that search will return a result 
      
      /*
       * This one recursively search for all (nested) group that this user belongs to
       * "member:1.2.840.113556.1.4.1941:" is a magic attribute, Reference: 
       * https://msdn.microsoft.com/en-us/library/aa746475(v=vs.85).aspx
       * However, this filter is usually slow in case your ad directory is large.
       */
      List<String> allGroups = ldapTemplate.search(
              query().searchScope(SearchScope.SUBTREE)
                      .where("member:1.2.840.113556.1.4.1941:").is(distinguishedName),
              (AttributesMapper<String>) attrs -> attrs.get("cn").get().toString()
      );
      

    对于 googlers:请注意,这具有 org.springframework.boot:spring-boot-starter-data-ldap 的依赖关系,以防有人需要 Bean 初始化代码:

    @Component
    @EnableConfigurationProperties
    public class Ldap {
        @Bean
        @ConfigurationProperties(prefix="ldap.contextSource")
        public LdapContextSource contextSource() {
            return new LdapContextSource();
        }
    
        @Bean
        public LdapTemplate ldapTemplate(ContextSource contextSource) {
            return new LdapTemplate(contextSource);
        }
    }
    

    在 application.yml 中使用以下配置模板:

    ldap:
        contextSource:
            url: ldap://your-ldap.server
            base: dc=Company,dc=Domain,dc=Controller
            userDn: username
            password: hunter2
            #you'll want connection polling set to true so ldapTemplate reuse the connection when searching recursively
            pooled: true
    

    当幻数的性能不好时:如果你的 ldap 目录很大,最后一个使用幻数的方法实际上很慢,在这种情况下递归搜索 ldap 会更快。这是一个帮助类,用于详尽搜索用户所属的所有组:

    public class LdapSearchRecursive {
        private final LdapTemplate ldapTemplate;
        private Set<String> groups;
    
        public LdapSearchRecursive(LdapTemplate ldapTemplate) {
            this.ldapTemplate = ldapTemplate;
            this.groups = new HashSet<>();
        }
    
        /**
         * Retrieve all groups that this user belongs to.
         */
        public Set<String> getAllGroupsForUserRecursively(String userCN) {
            List<String> distinguishedNames = this.ldapTemplate.search(
                    query().where("objectCategory").is("user").and(
                            query().where("sAMAccountName").is(userCN)
                                    .or(query().where("userPrincipalName").is(userCN))
                    ),
                    (AttributesMapper<String>) attrs -> attrs.get("distinguishedName").get().toString()
            );
    
            if (distinguishedNames.isEmpty()) {
                throw new UsernameNotFoundException("User not recognized in LDAP");
            }
    
            return this.getAllGroupsRecursivelyByUserDistinguishedName(distinguishedNames.get(0), null);
        }
    
        private Set<String> getAllGroupsRecursivelyByUserDistinguishedName(String dn, @Nullable String parentDN) {
            List<String> results = this.ldapTemplate.search(
                    query().where("member").is(dn),
                    (AttributesMapper<String>) attrs -> attrs.get("distinguishedName").get().toString()
            );
    
            for (String result : results) {
                if (!(result.equals(parentDN) //circular, ignore
                        || this.groups.contains(result) //duplicate, ignore
                        )) {
                    this.getAllGroupsRecursivelyByUserDistinguishedName(result, dn);
                }
            }
            this.groups.addAll(results);
    
            return this.groups;
        }
    }
    

    【讨论】:

    • 嘿!您的解决方案运行良好。但是在我的系统中使用幻数大约需要 5 秒,而像您建议的第二个答案那样手动解决它需要大约 50-60 秒。你对这种行为有什么解释吗? :)
    • @jksevend 这真的取决于您的 AD 森林的设计方式。另一个可能是:您是否将连接池设置为 true? (池化:在您的设置中为 true)。
    • 你的意思是ldap或spring设置与连接池?我真的不知道连接的设置,只是试图将 ldap 角色映射到 spring auhorities
    • @jksevend 如果您没有使用弹簧设置,您可以尝试调用 contextSource.setPooled(true); ,其中 contextSource 是您的 LdapContextSource (应该能够在您的代码中输入 ContextSource )。
    • 啊,好吧,我用contextSource 试过了,它降到了~8s。似乎使用幻数对我们的系统来说是一种更好的方法。或者可能需要一些额外的内部 ldap 设置。
    猜你喜欢
    • 1970-01-01
    • 2022-01-02
    • 2017-06-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-10-20
    • 2017-01-30
    相关资源
    最近更新 更多