SimpleNamingContextBuilder и LDAP от Spring - PullRequest
3 голосов
/ 30 ноября 2011

В настоящее время я пытаюсь разработать новый модуль для нашего существующего веб-приложения. Я пытаюсь добавить функциональность LDAP и у меня возникают проблемы при инициализации контекста LDAP, поскольку SimpleNamingContextBuilder регистрирует контекст, который не работает вместе с LdapTemplate .

В нашей весне applicationContext.xml у нас есть несколько поисков JNDI, поэтому перед запуском тестового примера я должен создать фиктивные JNDI-ресурсы, используя SimpleNamingContextBuilder в конструкторе тестовых случаев.

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

В нашем Spring application-context-test.xml мы имеем следующую конфигурацию ldap:

<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.

Ответы [ 3 ]

3 голосов
/ 29 октября 2013

Мне не удалось заставить SimpleNamingContextBuilder создать экземпляр DirContext, но использование пользовательского DirContextBuilder было решением, чтобы обойти это ограничение.

Смоделированный SimpleNamingContext в основном предназначен для предоставления связанных объектов, например,

InitialContext.doLookup(String name)

методов - чтобы те работали и обеспечивали надлежащую поддержку, например. Экземпляры LDAP DirContext, просто пропустите проверку «активированного» контекста - вы все равно загрузите свой код для применения activ (), так что это не проблема - по крайней мере, не для данного теста JNDI + LDAP.

Если эта проверка пропущена, код ищет ключ среды " java.naming.factory.initial " и, если среда пуста (это относится к InitialContext.doLookup (String name)), вы получите поддельный SimpleNamingContext с вашими связанными объектами.

Если вы используете LdapTemplate, среда не пуста, а ключ " java.naming.factory.initial " имеет значение " com.sun.jndi.ldap.LdapCtxFactory"или что-то подобное, по крайней мере (надеюсь) DirContext.

В этом случае вы получаете рабочий экземпляр DirContext обратно из вызова createInitialContextFactory, и поиск LdapTemplate выполняется успешно.

Так что просто создайте класс DirContextBuilder - возьмите код из SimpleNamingContextBuilder - вот так:

public class DirContextBuilder implements InitialContextFactoryBuilder {

...

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

Пропустите проверку для activated == null , и вы готовы протестировать связанные объекты JNDI и получить работающий шаблон LdapTemplate.

1 голос
/ 06 октября 2014

Я столкнулся с той же проблемой.Но преодолеть это можно с помощью следующего трюка

@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(){
    .....
}

Это свяжет вашу базу данных с некоторыми / name, а также позволит вам правильно подключиться к ldap.

0 голосов
/ 15 января 2019

Я тоже сталкивался с такой же проблемой.Я исследовал причины и внутреннее поведение в Java и SpringLdap, почему это происходит.Я пришел к следующему решению.

Я настроил ContextSource создание компонента для его решения.Этот метод является костылем и требует модификации конфигурации, которая проверяет тестовый режим.Но это работает.Ниже я представляю простой проект, демонстрирующий это.Для встраиваемого сервера 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 с использованием JNDI и 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;
    }
}

Боб с использованием переменной JNDI 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>
...