由于JDNI实现,Spring-LDAP过于严格

时间:2017-02-24 18:16:59

标签: java spring ldap spring-ldap javax

Gooday,

我对Spring-LDAP有一个相当奇怪的问题,它本质上看起来更像是一个JSNI问题,而不是Spring问题。

问题:

我有以下LDAP结构:

dc=company,dc=com,dc=productname
    |-o=TESTORG1
    |     |-ou=UserGroupName
    |     |-ou=users
    |           |-ou=user1@TESTORG1
    |-o=TESTORG2
    |     |-ou=User/Group/Name
    |     |-ou=users
    |           |-ou=user1@TESTORG2

所以我可以通过Spring给出的LdapTemplate非常巧妙地创建这个。但是,如果我尝试以递归方式删除组织,我会遇到一些奇怪的问题。

  @Test
  public void createSimpleOrganisationWithSpecialChars() throws NamingException {
    LdapOrganisation originalOrg = new LdapOrganisation("TESTORG2");
    LdapUserGroup userGroup = new LdapUserGroup("User/Group/Name");
    userGroup.addUser(new User("user1",originalOrg ));
    originalOrg.addUserGroup(userGroup);
    dataAccess.updateLdap(originalOrg);
    OrganisationDao org = new OrganisationDao(this.ldapTemplate);
    org.unbind(organisation,true);
  }

unbind的实现如下:

    public void unbind(LdapOrganisation organisation, boolean recursive) {
      if(organisation != null) {
        DirContextAdapter context = new DirContextAdapter(buildDn(organisation));
        mapToContext(organisation, context);
        ldapTemplate.unbind(context.getDn(), recursive);
      }
    }

现在如果我运行此代码,我会收到以下错误:

  org.springframework.ldap.InvalidNameException: Invalid name: "ou=User/Group/Name"; nested exception is javax.naming.InvalidNameException: Invalid name: "ou=User/Group/Name"

    at org.springframework.ldap.support.LdapUtils.convertLdapException(LdapUtils.java:136)
    at org.springframework.ldap.support.LdapUtils.newLdapName(LdapUtils.java:416)
    at org.springframework.ldap.core.LdapTemplate.deleteRecursively(LdapTemplate.java:1103)
    at org.springframework.ldap.core.LdapTemplate$25.executeWithContext(LdapTemplate.java:1074)
    at org.springframework.ldap.core.LdapTemplate.executeWithContext(LdapTemplate.java:817)
    at org.springframework.ldap.core.LdapTemplate.executeReadWrite(LdapTemplate.java:812)
    at org.springframework.ldap.core.LdapTemplate.doUnbindRecursively(LdapTemplate.java:1072)
    at org.springframework.ldap.core.LdapTemplate.unbind(LdapTemplate.java:1036)
    at net.thadir.dataservices.ldap.dao.OrganisationDao.unbind(OrganisationDao.java:108)
    at net.thadir.dataservices.ldapaccess.LdapDataAccessTest.cleanTest(LdapDataAccessTest.java:93)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:497)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
    at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:33)
    at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:86)
    at org.junit.rules.ExpectedException$ExpectedExceptionStatement.evaluate(ExpectedException.java:239)
    at org.junit.rules.RunRules.evaluate(RunRules.java:20)
    at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:84)
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:252)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:94)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
    at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
    at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:191)
    at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
    at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
    at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:51)
    at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:237)
    at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)
  Caused by: javax.naming.InvalidNameException: Invalid name: "ou=User/Group/Name"
    at javax.naming.ldap.Rfc2253Parser.parseAttrType(Rfc2253Parser.java:155)
    at javax.naming.ldap.Rfc2253Parser.doParse(Rfc2253Parser.java:108)
    at javax.naming.ldap.Rfc2253Parser.parseDn(Rfc2253Parser.java:70)
    at javax.naming.ldap.LdapName.parse(LdapName.java:785)
    at javax.naming.ldap.LdapName.<init>(LdapName.java:123)
    at org.springframework.ldap.support.LdapUtils.newLdapName(LdapUtils.java:414)
    ... 37 more

现在,如果我只是在测试中进行正常的解除绑定,那么它只是在用户组上进行。没有真正的问题..但实际上这是一个问题。因为看起来Spring用于递归删除了JDNI patern(那比LDAP允许的更严格)和正常的unbinds。它运作得很好。

现在我真的想让我的递归工作开箱即用。我尝试使用以下代码删除组:

在OrganisationDoa:

  public void unbind(LdapOrganisation organisation, boolean recursive) {
    if(organisation != null) {
      UserGroupDao ugu = new UserGroupDao(ldapTemplate);
      ugu.delete(organisation);
      DirContextAdapter context = new DirContextAdapter(buildDn(organisation));
      mapToContext(organisation, context);
      ldapTemplate.unbind(context.getDn(), recursive);
    }
  }

在UserGroupDao中:

  public void delete(LdapOrganisation ldapOrganisation, String ldapUserGroup) {
        ldapTemplate.unbind(buildDn(ldapOrganisation.getOrganisationShortname(), ldapUserGroup));
  }

  public void delete(LdapOrganisation ldapOrganisation) {
        for (LdapUserGroup userGroup : ldapOrganisation.getUserGroups()) {
              delete(ldapOrganisation,userGroup.getGroupName());
        }
  }

但这似乎不起作用,看到我仍然得到相同的堆栈跟踪(因此尝试在同一个步骤中执行它似乎不起作用)。

我一直在阅读所有这些RFC规范,看起来很旧。并且LDAP似乎只允许它,因为Spring似乎使用JDNI作为递归部分,我得到了这个问题。对预期会损坏的部件进行预清理似乎也无法正常工作。所以我很想听听一些建议/想法如何解决这个问题。

1 个答案:

答案 0 :(得分:0)

最后,我必须自己实现org.springframework.ldap.core.LdapTemplate以解决问题,因为修复程序如下:

package net.thadir.dataservices.ldap.util;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.ldap.NamingException;
import org.springframework.ldap.core.LdapTemplate;
import org.springframework.ldap.core.support.LdapContextSource;

import javax.naming.Binding;
import javax.naming.Name;
import javax.naming.NamingEnumeration;
import javax.naming.directory.DirContext;
import javax.naming.ldap.LdapName;

public class FixedLdapTemplate extends LdapTemplate {
  private static final Logger LOGGER = LoggerFactory.getLogger(FixedLdapTemplate.class);

  public FixedLdapTemplate(LdapContextSource contextSource) {
    super(contextSource);
  }

  /**
   * Delete all subcontexts including the current one recursively.
   *
   * @param ctx The context to use for deleting.
   * @param name The starting point to delete recursively.
   * @throws NamingException if any error occurs
   */
  @Override
  protected void deleteRecursively(DirContext ctx, Name name) {
    NamingEnumeration enumeration = null;
    try {
      enumeration = ctx.listBindings(name);
      while (enumeration.hasMore()) {
        Binding binding = (Binding) enumeration.next();
        String bindingName = binding.getName();
        LdapName childName = org.springframework.ldap.support.LdapUtils.newLdapName(fixJDNIBug(bindingName));
        childName.addAll(0, name);
        deleteRecursively(ctx, childName);
      }
      ctx.unbind(name);
      LOGGER.debug("Entry {} deleted", name);
    }
    catch (javax.naming.NamingException e) {
      throw org.springframework.ldap.support.LdapUtils.convertLdapException(e);
    }
    finally {
      try {
        if (enumeration != null) {
          enumeration.close();
        }
      }
      catch (Exception e) {
        LOGGER.trace("Never mind this", e);
      }
    }
  }

  /**
   * Becouse somhow JDNI things that if you have a binding with some special characters it adds some extra " to the beginnin and the end. We have to remove it.
   *
   * @param bindingName
   * @return
   */
  private static String fixJDNIBug(String bindingName) {
    String result = bindingName;
    if(result.contains("/") && result.startsWith("\"") && result.endsWith("\"")) {
      result = result.substring(1,bindingName.length()-1);
    }
    return result;
  }
}

有问题的bug在过去十年中一直是JDNI的一部分,所以我希望它很快就会在Spring中修复。令我感到遗憾的是,我必须为她们提供一些小课程。