Spring-由于 JDNI 实现,LDAP 过于严格
Spring-LDAP too strict because of JDNI implementation
再见,
我在使用 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
所以我可以使用 LdapTemplate 通过 Spring 非常巧妙地创建这一切。但是,如果我尝试递归删除组织,我 运行 会遇到一些奇怪的问题。
@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);
}
解除绑定的实现如下:
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.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.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.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access[=14=]0(ParentRunner.java:58)
at org.junit.runners.ParentRunner.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 模式(这比 LDAP 允许的更严格)和正常的解除绑定。它完美运行。
现在我真的想让我的递归工作开箱即用。我尝试使用以下代码删除组:
在 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 用于我遇到此问题的递归部分。对您预计会损坏的部件进行预清理似乎效果不佳。所以我很想听听一些 suggestions/ideas 如何解决这个问题。
最后我不得不自己实现 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;
}
}
有问题的错误在过去 10 年里一直是 JDNI 的一部分,所以我希望它很快会在 Spring 中自行修复。因为很遗憾我不得不在那里扩展 类 来做一件小事。
再见,
我在使用 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
所以我可以使用 LdapTemplate 通过 Spring 非常巧妙地创建这一切。但是,如果我尝试递归删除组织,我 运行 会遇到一些奇怪的问题。
@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);
}
解除绑定的实现如下:
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.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.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.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access[=14=]0(ParentRunner.java:58)
at org.junit.runners.ParentRunner.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 模式(这比 LDAP 允许的更严格)和正常的解除绑定。它完美运行。
现在我真的想让我的递归工作开箱即用。我尝试使用以下代码删除组:
在 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 用于我遇到此问题的递归部分。对您预计会损坏的部件进行预清理似乎效果不佳。所以我很想听听一些 suggestions/ideas 如何解决这个问题。
最后我不得不自己实现 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;
}
}
有问题的错误在过去 10 年里一直是 JDNI 的一部分,所以我希望它很快会在 Spring 中自行修复。因为很遗憾我不得不在那里扩展 类 来做一件小事。