Spring 的 SimpleNamingContextBuilder 和 LDAP

发布于 2024-12-18 15:54:21 字数 3711 浏览 2 评论 0原文

我目前正在尝试为我们现有的网络应用程序开发一个新模块。我正在尝试添加 LDAP 功能,但在初始化 LDAP 上下文时遇到问题,因为 SimpleNamingContextBuilder 注册的上下文不能与 LdapTemplate

在我们的 spring applicationContext.xml 中,我们有多个 JNDI 查找,因此在运行测试用例之前,我必须使用 SimpleNamingContextBuilder 在测试用例构造函数中。

SimpleNamingContextBuilder builder = new SimpleNamingContextBuilder();
builder.bind("someJNDIname",someObject); //e.g. for some datasource
builder.activate();

在我们的 Spring application-context-test.xml 中,我们有以下 ldapConfiguration:

<bean id="ldapContextSource" class="org.springframework.ldap.core.support.LdapContextSource">
    <property name="url" value="ldap://ourserver:389" />
    <property name="base" value="CN=Groups,CN=ourcompany,DC=com" />
    <property name="userDn" value="CN=binduser" />
    <property name="password" value="password" />
</bean>

<bean id="ldapTemplate" class="org.springframework.ldap.core.LdapTemplate">
    <constructor-arg ref="ldapContextSource" />
</bean>

我们运行测试用例:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:application-context-test.xml"})
public class TestClass {
    public TestClass(){
       .. //init the SimpleNamingContextBuilder
    }

    @Autowired
    private LdapTemplate template;

    @Test
    public void someTestcase(){
        ldapTemplate.search("", "(objectclass=user)" ,new LdapUserMapper());
    }
 }

由于 SimpleNamingContextBuilder 已经注册了一个简单的 InitialContext,我收到以下错误:

org.springframework.ldap.NotContextException: DirContext object is required.; nested exception is javax.naming.NotContextException: DirContext object is required.
  at org.springframework.ldap.support.LdapUtils.convertLdapException(LdapUtils.java:198)
  at org.springframework.ldap.core.LdapTemplate.search(LdapTemplate.java:319)
  at org.springframework.ldap.core.LdapTemplate.search(LdapTemplate.java:259)
  at org.springframework.ldap.core.LdapTemplate.search(LdapTemplate.java:571)
  at org.springframework.ldap.core.LdapTemplate.search(LdapTemplate.java:556)
  at org.springframework.ldap.core.LdapTemplate.search(LdapTemplate.java:411)
  at org.springframework.ldap.core.LdapTemplate.search(LdapTemplate.java:431)
  at org.springframework.ldap.core.LdapTemplate.search(LdapTemplate.java:451)
  at  com.somecompany.TestClass.someTestcase(TestClass.java:30)
      [...]
  at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)
Caused by: javax.naming.NotContextException: DirContext object is required.
  at javax.naming.directory.InitialDirContext.castToDirContext(InitialDirContext.java:106)
  at javax.naming.directory.InitialDirContext.getURLOrDefaultInitDirCtx(InitialDirContext.java:112)
  at javax.naming.directory.InitialDirContext.search(InitialDirContext.java:245)
  at org.springframework.ldap.core.LdapTemplate$4.executeSearch(LdapTemplate.java:253)
  at org.springframework.ldap.core.LdapTemplate.search(LdapTemplate.java:293)
  ... 35 more

该错误告诉我, LDAP 需要 DirContext。如何让 SimpleNamingContextBuilder 创建和使用这样的 目录上下文

如果我不注册 SimpleNamingContextBuilder,则创建 LDAPTemplate 将会起作用。但是,我会遇到其他问题,因为应用程序的其他部分需要 JNDI 查找。

I'm currently trying to develop a new module for our existing web application. I'm trying to add LDAP functionality and have problems initializing the LDAP context as the SimpleNamingContextBuilder registers a context that is not working together with the LdapTemplate.

In our spring applicationContext.xml we have several JNDI lookups, so before running a test case I have to create mock JNDI-Resources using the SimpleNamingContextBuilder in the test cases constructor.

SimpleNamingContextBuilder builder = new SimpleNamingContextBuilder();
builder.bind("someJNDIname",someObject); //e.g. for some datasource
builder.activate();

In our Spring application-context-test.xml we have the following ldapConfiguration:

<bean id="ldapContextSource" class="org.springframework.ldap.core.support.LdapContextSource">
    <property name="url" value="ldap://ourserver:389" />
    <property name="base" value="CN=Groups,CN=ourcompany,DC=com" />
    <property name="userDn" value="CN=binduser" />
    <property name="password" value="password" />
</bean>

<bean id="ldapTemplate" class="org.springframework.ldap.core.LdapTemplate">
    <constructor-arg ref="ldapContextSource" />
</bean>

We run the testcase with:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:application-context-test.xml"})
public class TestClass {
    public TestClass(){
       .. //init the SimpleNamingContextBuilder
    }

    @Autowired
    private LdapTemplate template;

    @Test
    public void someTestcase(){
        ldapTemplate.search("", "(objectclass=user)" ,new LdapUserMapper());
    }
 }

As the SimpleNamingContextBuilder is already registering a simple InitialContext I get the following error:

org.springframework.ldap.NotContextException: DirContext object is required.; nested exception is javax.naming.NotContextException: DirContext object is required.
  at org.springframework.ldap.support.LdapUtils.convertLdapException(LdapUtils.java:198)
  at org.springframework.ldap.core.LdapTemplate.search(LdapTemplate.java:319)
  at org.springframework.ldap.core.LdapTemplate.search(LdapTemplate.java:259)
  at org.springframework.ldap.core.LdapTemplate.search(LdapTemplate.java:571)
  at org.springframework.ldap.core.LdapTemplate.search(LdapTemplate.java:556)
  at org.springframework.ldap.core.LdapTemplate.search(LdapTemplate.java:411)
  at org.springframework.ldap.core.LdapTemplate.search(LdapTemplate.java:431)
  at org.springframework.ldap.core.LdapTemplate.search(LdapTemplate.java:451)
  at  com.somecompany.TestClass.someTestcase(TestClass.java:30)
      [...]
  at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)
Caused by: javax.naming.NotContextException: DirContext object is required.
  at javax.naming.directory.InitialDirContext.castToDirContext(InitialDirContext.java:106)
  at javax.naming.directory.InitialDirContext.getURLOrDefaultInitDirCtx(InitialDirContext.java:112)
  at javax.naming.directory.InitialDirContext.search(InitialDirContext.java:245)
  at org.springframework.ldap.core.LdapTemplate$4.executeSearch(LdapTemplate.java:253)
  at org.springframework.ldap.core.LdapTemplate.search(LdapTemplate.java:293)
  ... 35 more

The error tells me that the LDAP requires a DirContext. How can I get the SimpleNamingContextBuilder to create and use such a DirContext.

If I don't register the SimpleNamingContextBuilder then creating the LDAPTemplate will work. However I will run into other problems as other parts of the application require the JNDI lookups.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

欢烬 2024-12-25 15:54:22

我没有设法让 SimpleNamingContextBuilder 创建 DirContext 的实例,但使用自定义 DirContextBuilder 是解决此限制的解决方案。

模拟的 SimpleNamingContext 主要是通过例如方法提供绑定对象

InitialContext.doLookup(String name)

- 让这些方法工作并为例如 LDAP DirContext 实例提供适当的支持,只需省略对“已激活”上下文的检查 - 您将引导代码应用 activate()无论如何,所以这不是问题 - 至少对于给定的 JNDI + LDAP 测试用例来说不是。

如果缺少此检查,代码将查找“java.naming.factory.initial”环境键,如果环境为空(InitialContext.doLookup(String name) 就是这种情况),您将得到使用绑定对象模拟 SimpleNamingContext。

如果您使用 LdapTemplate,则环境不为空,并且键“java.naming.factory.initial”设置为“com.sun.jndi.ldap.LdapCtxFactory”或类似的东西至少(希望)是一个 DirContext。

在这种情况下,您从 createInitialContextFactory 调用返回一个工作 DirContext 实例,并且 LdapTemplate 查找成功。

因此,只需创建一个类 DirContextBuilder - 从 SimpleNamingContextBuilder 中获取代码 - 如下所示:

public class DirContextBuilder implements InitialContextFactoryBuilder {

...

public InitialContextFactory createInitialContextFactory(Hashtable<?,?> environment) {
    if (environment != null) {
...
}

省略对 activated == null 的检查,然后您就可以测试绑定的 JNDI 对象并拥有一个可用的 LdapTemplate。

I did not manage to get the SimpleNamingContextBuilder to create an instance of DirContext, but using a custom DirContextBuilder was the solution to get around this limitation.

The mocked SimpleNamingContext is mainly there to provide bound objects via e.g.

InitialContext.doLookup(String name)

methods - to let those ones work and provide proper support for e.g. LDAP DirContext instances, just omit the check for the "activated" context - you will bootstrap your code to apply activate() anyway, so this is no problem - at least not for the given JNDI + LDAP test case.

Missing this check, the code looks for the "java.naming.factory.initial" environment key and if the environment is empty (this is the case for InitialContext.doLookup(String name)) you get the mocked SimpleNamingContext with your bound objects.

If you use the LdapTemplate the environment is not empty and the key "java.naming.factory.initial" is set to "com.sun.jndi.ldap.LdapCtxFactory" or something similar which is at least (hopefully) a DirContext.

In this case you get a working DirContext instance back from the createInitialContextFactory call and the LdapTemplate lookup is successful.

So just create a class DirContextBuilder - take the code from SimpleNamingContextBuilder - like this:

public class DirContextBuilder implements InitialContextFactoryBuilder {

...

public InitialContextFactory createInitialContextFactory(Hashtable<?,?> environment) {
    if (environment != null) {
...
}

Omit the check for activated == null and you are ready to test your bound JNDI objects and have a working LdapTemplate.

绻影浮沉 2024-12-25 15:54:22

我遇到了同样的问题。但可以用下面的技巧克服它,

@BeforeClass
public static void setUp(){
    OracleDataSource ods = null;
    try {
        ods= new OracleDataSource();
    } catch (SQLException e) {
        e.printStackTrace();
    }
    ods.setURL("jdbc:oracle:thin:@(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=....;
    ods.setUser(..);
    ods.setPassword(..);        

    SimpleNamingContextBuilder builder = null;
    try {
        builder = SimpleNamingContextBuilder.emptyActivatedContextBuilder();
        builder.bind("some/name", ods);
    } catch (NamingException e) {
        e.printStackTrace();
    }
}

@Before
public void beforeTest(){
      SimpleNamingContextBuilder.getCurrentContextBuilder().deactivate();
}

@Test
public void yourTest(){
    .....
}

这会将您的数据库与 some/name 绑定,并让您正确连接到 ldap。

I faced the same issue. But overcome it with the below trick

@BeforeClass
public static void setUp(){
    OracleDataSource ods = null;
    try {
        ods= new OracleDataSource();
    } catch (SQLException e) {
        e.printStackTrace();
    }
    ods.setURL("jdbc:oracle:thin:@(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=....;
    ods.setUser(..);
    ods.setPassword(..);        

    SimpleNamingContextBuilder builder = null;
    try {
        builder = SimpleNamingContextBuilder.emptyActivatedContextBuilder();
        builder.bind("some/name", ods);
    } catch (NamingException e) {
        e.printStackTrace();
    }
}

@Before
public void beforeTest(){
      SimpleNamingContextBuilder.getCurrentContextBuilder().deactivate();
}

@Test
public void yourTest(){
    .....
}

This will bind your database with some/name and also let you to connect to the ldap correctly.

总以为 2024-12-25 15:54:22

我也面临同样的问题。我研究了 Java 和 SpringLdap 中发生这种情况的原因和内部行为。我做出了以下决定。

我定制了 ContextSource bean 创建来解决这个问题。这种方法很困难,需要修改检查测试模式的配置。但它有效。
下面我用一个简单的项目来演示一下。对于嵌入式 LDAP 服务器,我使用了 Apache Directory Server

CommonConfig.java 包含这个拐杖:

package com.stackoverflow.question8325740.config;


import com.stackoverflow.question8325740.JndiExplorer;
import com.stackoverflow.question8325740.LdapSettings;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.ldap.core.ContextSource;
import org.springframework.ldap.core.LdapOperations;
import org.springframework.ldap.core.LdapTemplate;
import org.springframework.ldap.core.support.AbstractContextSource;
import org.springframework.ldap.core.support.LdapContextSource;

import javax.naming.Context;
import javax.naming.NamingException;
import javax.naming.NoInitialContextException;
import javax.naming.directory.DirContext;
import javax.naming.ldap.Control;
import javax.naming.ldap.InitialLdapContext;
import javax.naming.spi.InitialContextFactory;
import javax.naming.spi.NamingManager;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;

@Configuration
public class CommonConfig {

    private static class CustomLdapContextSource extends AbstractContextSource {
        @Override
        protected DirContext getDirContextInstance(Hashtable<String, Object> environment) throws NamingException {
            return new CustomLdapContext(environment, null);
        }
    }

    private static class CustomLdapContext extends InitialLdapContext {
        public CustomLdapContext() throws NamingException {
        }

        public CustomLdapContext(Hashtable<?, ?> environment, Control[] connCtls) throws NamingException {
            super(environment, connCtls);
        }

        @Override
        protected Context getDefaultInitCtx() throws NamingException {
            String className = "com.sun.jndi.ldap.LdapCtxFactory";
            InitialContextFactory factory;
            try {
                factory = (InitialContextFactory) Class.forName(className).newInstance();
            } catch (Exception e) {
                NoInitialContextException ne =
                        new NoInitialContextException(
                                "Cannot instantiate class: " + className);
                ne.setRootCause(e);
                throw ne;
            }
            return factory.getInitialContext(myProps);
        }


    }

    private static boolean checkTestMode() {
        //checking test mode using reflection in order to not collapse in real execution
        try {
            Class clazz = Class.forName("org.springframework.mock.jndi.SimpleNamingContextBuilder");
            Object result = clazz.getMethod("getCurrentContextBuilder").invoke(null);
            return NamingManager.hasInitialContextFactoryBuilder() && result != null;
        } catch (Exception e) {
            return false;
        }


    }

    @Bean
    @Autowired
    public ContextSource ldapContextSource(LdapSettings ldapSettings) {
        AbstractContextSource contextSource;
        if (checkTestMode()) {
            contextSource = new CustomLdapContextSource();
        } else {
            contextSource = new LdapContextSource();
        }
        contextSource.setUrl(ldapSettings.getUrl());
        contextSource.setUserDn(ldapSettings.getLogin());
        contextSource.setPassword(ldapSettings.getPassword());
        contextSource.setPooled(true);
        contextSource.setAnonymousReadOnly(false);

        Map<String, Object> baseEnvironmentProperties = new HashMap<String, Object>();
        baseEnvironmentProperties.put(Context.SECURITY_AUTHENTICATION, "simple");
        baseEnvironmentProperties.put(Context.REFERRAL, "follow");
        contextSource.setBaseEnvironmentProperties(baseEnvironmentProperties);
        return contextSource;
    }

    @Bean
    @Autowired
    public LdapOperations ldapTemplate(ContextSource ldapContextSource) {
        return new LdapTemplate(ldapContextSource);
    }

    @Bean
    public JndiExplorer jndiExplorer() {
        return new JndiExplorer();
    }

}

MainTest.java,使用 JNDILdapOperations

package com.stackoverflow.question8325740;

import com.stackoverflow.question8325740.config.CommonConfig;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.ldap.core.AttributesMapper;
import org.springframework.ldap.core.LdapOperations;
import org.springframework.ldap.query.LdapQueryBuilder;
import org.springframework.ldap.test.LdapTestUtils;
import org.springframework.mock.jndi.SimpleNamingContextBuilder;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit.jupiter.SpringExtension;

import javax.naming.NamingException;
import javax.naming.directory.Attributes;
import javax.naming.directory.BasicAttributes;
import java.util.List;


@ExtendWith(SpringExtension.class)
@ContextConfiguration(classes = {ApacheDsEmbededConfiguration.class, CommonConfig.class})
public class MainTest {
    public static final String TEST_VALUE = "testValue";


    private static SimpleNamingContextBuilder builder = new SimpleNamingContextBuilder();

    @BeforeAll
    public static void setUp() throws Exception {
        builder.bind(JndiExplorer.JNDI_TEST, TEST_VALUE);
        builder.activate();
        LdapTestUtils.startEmbeddedServer(ApacheDsEmbededConfiguration.PORT, ApacheDsEmbededConfiguration.DEFAULT_PARTITION_SUFFIX, "test");
        Thread.sleep(1000);
    }

    @AfterAll
    public static void shutdown() throws Exception {
        LdapTestUtils.shutdownEmbeddedServer();
        builder.deactivate();
    }


    @Autowired
    private JndiExplorer jndiExplorer;

    @Autowired
    private LdapOperations ldapOperations;

    @Test
    public void testLdapTemplateWithSimpleJndi() {
        Assertions.assertEquals(TEST_VALUE, jndiExplorer.getValue());
        String cn = "testCN";
        String sn = "testSN";
        Attributes attrs = new BasicAttributes();
        attrs.put("objectClass", "inetOrgPerson");
        attrs.put("cn", cn);
        attrs.put("sn", sn);
        ldapOperations.bind("cn=" + cn + "," + ApacheDsEmbededConfiguration.DEFAULT_PARTITION_SUFFIX, null, attrs);

        AttributesMapper<String> mapper = new AttributesMapper<String>() {
            @Override
            public String mapFromAttributes(Attributes attributes) throws NamingException {
                return (String) attributes.get("sn").get();
            }
        };
        List<String> sns = ldapOperations.search(LdapQueryBuilder.query().base(ApacheDsEmbededConfiguration.DEFAULT_PARTITION_SUFFIX).attributes("*").where("sn").is(sn), mapper);

        Assertions.assertEquals(1, sns.size());
        String resultSn = sns.get(0);
        Assertions.assertEquals(sn, resultSn);

    }

}

ApacheDsEmbededConfiguration.java< /code>:

package com.stackoverflow.question8325740;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class ApacheDsEmbededConfiguration {

    //default password
    static final String PASSWORD = "secret";
    //default admin DN
    static final String PRINCIPAL = "uid=admin,ou=system";

    static final String DEFAULT_PARTITION_SUFFIX = "dc=stackoverflow,dc=com";
    static final int PORT = 1888;

    @Bean
    public LdapSettings ldapSettings() {
        LdapSettings settings = new LdapSettings();
        settings.setUrl("ldap://localhost:" + PORT);
        settings.setLogin(PRINCIPAL);
        settings.setPassword(PASSWORD);
        return settings;

    }

}

Pojo LdapSettings.java:

package com.stackoverflow.question8325740;


public class LdapSettings {
    private String url;
    private String login;
    private String password;

    public String getUrl() {
        return url;
    }

    public void setUrl(String url) {
        this.url = url;
    }

    public String getLogin() {
        return login;
    }

    public void setLogin(String login) {
        this.login = login;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }
}

使用 JNDI 变量的 Bean JndiExplorer.java:

package com.stackoverflow.question8325740;

import javax.annotation.Resource;

public class JndiExplorer {
    public static final String JNDI_TEST = "com/anything";

    @Resource(mappedName = JNDI_TEST)
    private String value;

    public String getValue() {
        return value;
    }

    public void setValue(String value) {
        this.value = value;
    }
}

以及pom.xml:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.stackoverflow</groupId>
    <artifactId>question-8325740</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
        <junit.version>5.3.2</junit.version>
        <spring.version>5.1.4.RELEASE</spring.version>
        <spring.ldap.version>2.3.2.RELEASE</spring.ldap.version>
        <apacheDirectoryService.version>1.5.5</apacheDirectoryService.version>
        <apacheDirectoryService.shared.version>0.9.15</apacheDirectoryService.shared.version>
    </properties>
    <dependencies>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.ldap</groupId>
            <artifactId>spring-ldap-core</artifactId>
            <version>${spring.ldap.version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>${spring.version}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-engine</artifactId>
            <version>${junit.version}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.ldap</groupId>
            <artifactId>spring-ldap-test</artifactId>
            <version>${spring.ldap.version}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.apache.directory.server</groupId>
            <artifactId>apacheds-core</artifactId>
            <version>${apacheDirectoryService.version}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.apache.directory.server</groupId>
            <artifactId>apacheds-core-entry</artifactId>
            <version>${apacheDirectoryService.version}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.apache.directory.server</groupId>
            <artifactId>apacheds-protocol-shared</artifactId>
            <version>${apacheDirectoryService.version}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.apache.directory.server</groupId>
            <artifactId>apacheds-protocol-ldap</artifactId>
            <version>${apacheDirectoryService.version}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.apache.directory.server</groupId>
            <artifactId>apacheds-server-jndi</artifactId>
            <version>${apacheDirectoryService.version}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.apache.directory.shared</groupId>
            <artifactId>shared-ldap</artifactId>
            <version>${apacheDirectoryService.shared.version}</version>
            <scope>test</scope>
        </dependency>
    </dependencies>
   <build>
      <plugins>
        <plugin>
             <groupId>org.apache.maven.plugins</groupId>
             <artifactId>maven-surefire-plugin</artifactId>
             <version>3.0.0-M3</version>
         </plugin>
      </plugins>
    </build>
</project>

I also faced the same issue. I researched causes and internal behaviour in Java and SpringLdap why it happens. I came to the following decision.

I customized ContextSource bean creation in order to solve it. This method is crutch and requires modification config that checking test mode. But it works.
Below I present simple project demostrated it. For embeded LDAP server I used Apache Directory Server.

CommonConfig.java consisted this crutch:

package com.stackoverflow.question8325740.config;


import com.stackoverflow.question8325740.JndiExplorer;
import com.stackoverflow.question8325740.LdapSettings;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.ldap.core.ContextSource;
import org.springframework.ldap.core.LdapOperations;
import org.springframework.ldap.core.LdapTemplate;
import org.springframework.ldap.core.support.AbstractContextSource;
import org.springframework.ldap.core.support.LdapContextSource;

import javax.naming.Context;
import javax.naming.NamingException;
import javax.naming.NoInitialContextException;
import javax.naming.directory.DirContext;
import javax.naming.ldap.Control;
import javax.naming.ldap.InitialLdapContext;
import javax.naming.spi.InitialContextFactory;
import javax.naming.spi.NamingManager;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;

@Configuration
public class CommonConfig {

    private static class CustomLdapContextSource extends AbstractContextSource {
        @Override
        protected DirContext getDirContextInstance(Hashtable<String, Object> environment) throws NamingException {
            return new CustomLdapContext(environment, null);
        }
    }

    private static class CustomLdapContext extends InitialLdapContext {
        public CustomLdapContext() throws NamingException {
        }

        public CustomLdapContext(Hashtable<?, ?> environment, Control[] connCtls) throws NamingException {
            super(environment, connCtls);
        }

        @Override
        protected Context getDefaultInitCtx() throws NamingException {
            String className = "com.sun.jndi.ldap.LdapCtxFactory";
            InitialContextFactory factory;
            try {
                factory = (InitialContextFactory) Class.forName(className).newInstance();
            } catch (Exception e) {
                NoInitialContextException ne =
                        new NoInitialContextException(
                                "Cannot instantiate class: " + className);
                ne.setRootCause(e);
                throw ne;
            }
            return factory.getInitialContext(myProps);
        }


    }

    private static boolean checkTestMode() {
        //checking test mode using reflection in order to not collapse in real execution
        try {
            Class clazz = Class.forName("org.springframework.mock.jndi.SimpleNamingContextBuilder");
            Object result = clazz.getMethod("getCurrentContextBuilder").invoke(null);
            return NamingManager.hasInitialContextFactoryBuilder() && result != null;
        } catch (Exception e) {
            return false;
        }


    }

    @Bean
    @Autowired
    public ContextSource ldapContextSource(LdapSettings ldapSettings) {
        AbstractContextSource contextSource;
        if (checkTestMode()) {
            contextSource = new CustomLdapContextSource();
        } else {
            contextSource = new LdapContextSource();
        }
        contextSource.setUrl(ldapSettings.getUrl());
        contextSource.setUserDn(ldapSettings.getLogin());
        contextSource.setPassword(ldapSettings.getPassword());
        contextSource.setPooled(true);
        contextSource.setAnonymousReadOnly(false);

        Map<String, Object> baseEnvironmentProperties = new HashMap<String, Object>();
        baseEnvironmentProperties.put(Context.SECURITY_AUTHENTICATION, "simple");
        baseEnvironmentProperties.put(Context.REFERRAL, "follow");
        contextSource.setBaseEnvironmentProperties(baseEnvironmentProperties);
        return contextSource;
    }

    @Bean
    @Autowired
    public LdapOperations ldapTemplate(ContextSource ldapContextSource) {
        return new LdapTemplate(ldapContextSource);
    }

    @Bean
    public JndiExplorer jndiExplorer() {
        return new JndiExplorer();
    }

}

MainTest.java using JNDI and LdapOperations:

package com.stackoverflow.question8325740;

import com.stackoverflow.question8325740.config.CommonConfig;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.ldap.core.AttributesMapper;
import org.springframework.ldap.core.LdapOperations;
import org.springframework.ldap.query.LdapQueryBuilder;
import org.springframework.ldap.test.LdapTestUtils;
import org.springframework.mock.jndi.SimpleNamingContextBuilder;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit.jupiter.SpringExtension;

import javax.naming.NamingException;
import javax.naming.directory.Attributes;
import javax.naming.directory.BasicAttributes;
import java.util.List;


@ExtendWith(SpringExtension.class)
@ContextConfiguration(classes = {ApacheDsEmbededConfiguration.class, CommonConfig.class})
public class MainTest {
    public static final String TEST_VALUE = "testValue";


    private static SimpleNamingContextBuilder builder = new SimpleNamingContextBuilder();

    @BeforeAll
    public static void setUp() throws Exception {
        builder.bind(JndiExplorer.JNDI_TEST, TEST_VALUE);
        builder.activate();
        LdapTestUtils.startEmbeddedServer(ApacheDsEmbededConfiguration.PORT, ApacheDsEmbededConfiguration.DEFAULT_PARTITION_SUFFIX, "test");
        Thread.sleep(1000);
    }

    @AfterAll
    public static void shutdown() throws Exception {
        LdapTestUtils.shutdownEmbeddedServer();
        builder.deactivate();
    }


    @Autowired
    private JndiExplorer jndiExplorer;

    @Autowired
    private LdapOperations ldapOperations;

    @Test
    public void testLdapTemplateWithSimpleJndi() {
        Assertions.assertEquals(TEST_VALUE, jndiExplorer.getValue());
        String cn = "testCN";
        String sn = "testSN";
        Attributes attrs = new BasicAttributes();
        attrs.put("objectClass", "inetOrgPerson");
        attrs.put("cn", cn);
        attrs.put("sn", sn);
        ldapOperations.bind("cn=" + cn + "," + ApacheDsEmbededConfiguration.DEFAULT_PARTITION_SUFFIX, null, attrs);

        AttributesMapper<String> mapper = new AttributesMapper<String>() {
            @Override
            public String mapFromAttributes(Attributes attributes) throws NamingException {
                return (String) attributes.get("sn").get();
            }
        };
        List<String> sns = ldapOperations.search(LdapQueryBuilder.query().base(ApacheDsEmbededConfiguration.DEFAULT_PARTITION_SUFFIX).attributes("*").where("sn").is(sn), mapper);

        Assertions.assertEquals(1, sns.size());
        String resultSn = sns.get(0);
        Assertions.assertEquals(sn, resultSn);

    }

}

ApacheDsEmbededConfiguration.java:

package com.stackoverflow.question8325740;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class ApacheDsEmbededConfiguration {

    //default password
    static final String PASSWORD = "secret";
    //default admin DN
    static final String PRINCIPAL = "uid=admin,ou=system";

    static final String DEFAULT_PARTITION_SUFFIX = "dc=stackoverflow,dc=com";
    static final int PORT = 1888;

    @Bean
    public LdapSettings ldapSettings() {
        LdapSettings settings = new LdapSettings();
        settings.setUrl("ldap://localhost:" + PORT);
        settings.setLogin(PRINCIPAL);
        settings.setPassword(PASSWORD);
        return settings;

    }

}

Pojo LdapSettings.java:

package com.stackoverflow.question8325740;


public class LdapSettings {
    private String url;
    private String login;
    private String password;

    public String getUrl() {
        return url;
    }

    public void setUrl(String url) {
        this.url = url;
    }

    public String getLogin() {
        return login;
    }

    public void setLogin(String login) {
        this.login = login;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }
}

Bean using JNDI-variable JndiExplorer.java:

package com.stackoverflow.question8325740;

import javax.annotation.Resource;

public class JndiExplorer {
    public static final String JNDI_TEST = "com/anything";

    @Resource(mappedName = JNDI_TEST)
    private String value;

    public String getValue() {
        return value;
    }

    public void setValue(String value) {
        this.value = value;
    }
}

And pom.xml:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.stackoverflow</groupId>
    <artifactId>question-8325740</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
        <junit.version>5.3.2</junit.version>
        <spring.version>5.1.4.RELEASE</spring.version>
        <spring.ldap.version>2.3.2.RELEASE</spring.ldap.version>
        <apacheDirectoryService.version>1.5.5</apacheDirectoryService.version>
        <apacheDirectoryService.shared.version>0.9.15</apacheDirectoryService.shared.version>
    </properties>
    <dependencies>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.ldap</groupId>
            <artifactId>spring-ldap-core</artifactId>
            <version>${spring.ldap.version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>${spring.version}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-engine</artifactId>
            <version>${junit.version}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.ldap</groupId>
            <artifactId>spring-ldap-test</artifactId>
            <version>${spring.ldap.version}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.apache.directory.server</groupId>
            <artifactId>apacheds-core</artifactId>
            <version>${apacheDirectoryService.version}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.apache.directory.server</groupId>
            <artifactId>apacheds-core-entry</artifactId>
            <version>${apacheDirectoryService.version}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.apache.directory.server</groupId>
            <artifactId>apacheds-protocol-shared</artifactId>
            <version>${apacheDirectoryService.version}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.apache.directory.server</groupId>
            <artifactId>apacheds-protocol-ldap</artifactId>
            <version>${apacheDirectoryService.version}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.apache.directory.server</groupId>
            <artifactId>apacheds-server-jndi</artifactId>
            <version>${apacheDirectoryService.version}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.apache.directory.shared</groupId>
            <artifactId>shared-ldap</artifactId>
            <version>${apacheDirectoryService.shared.version}</version>
            <scope>test</scope>
        </dependency>
    </dependencies>
   <build>
      <plugins>
        <plugin>
             <groupId>org.apache.maven.plugins</groupId>
             <artifactId>maven-surefire-plugin</artifactId>
             <version>3.0.0-M3</version>
         </plugin>
      </plugins>
    </build>
</project>
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文