4

現在、既存の Web アプリケーション用の新しいモジュールを開発しようとしています。LDAP 機能を追加しようとしていますが、 SimpleNamingContextBuilder がLdapTemplateと連携していないコンテキストを登録するため、LDAP コンテキストの初期化に問題があります。

applicationContext.xmlにはいくつかの JNDI ルックアップがあるため、テスト ケースを実行する前に、テスト ケース コンストラクターでSimpleNamingContextBuilderを使用してモック JNDI リソースを作成する必要があります。

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

Springapplication-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 でそのようなDirContextを作成して使用するにはどうすればよいですか。

SimpleNamingContextBuilder を登録しないと、作成LDAPTemplateが機能します。ただし、アプリケーションの他の部分で JNDI ルックアップが必要になるため、別の問題が発生します。

4

3 に答える 3

0

私も同じ問題に直面しました。原因と内部動作、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.javaJNDIとを使用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;

    }

}

ポジョ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>
于 2019-01-15T15:48:36.040 に答える