Best practice for configuring Spring LdapTemplate via annotations instead of XML?
Asked Answered
A

2

36

For a Spring Boot application, I successfully configured a Spring LdapTemplate using annotations, including the LdapContextSource dependency with @Values from application.properties. (Woot! I couldn't find an example, so maybe this will help others.)

The snippets (below) setup the context source, inject it into an LdapTemplate, and autowire that into my DirectoryService.

Is there a better/cleaner way to setup the ContextSource in a Spring Boot app?

application.properties (on the classpath):

ldap.url=ldap://server.domain.com:389
ldap.base:OU=Employees,OU=Users,DC=domain,DC=com
ldap.username:CN=myuserid,OU=employees,OU=Users,DC=domain,DC=com
ldap.password:secretthingy

MyLdapContextSource.java :

@Component
public class MyLdapContextSource extends LdapContextSource implements ContextSource {

    @Value("${ldap.url}")
    @Override
    public void setUrl(String url) { super.setUrl(url);  }

    @Value("${ldap.base}")
    @Override
    public void setBase(String base) {super.setBase(base); }

    @Value("${ldap.username}")
    @Override
    public void setUserDn(String userDn) {super.setUserDn(userDn); }

    @Value("${ldap.password}")
    @Override
    public void setPassword(String password) { super.setPassword(password); }
}

MyLdapTemplate.java:

@Component
public class MyLdapTemplate extends LdapTemplate {

    @Autowired
    public MyLdapTemplate(ContextSource contextSource) { super(contextSource); }
}

DirectoryService.java:

@Service
public class DirectoryService {

    private final LdapTemplate ldapTemplate;

    @Value("${ldap.base}")
    private String BASE_DN;

    @Autowired
    public DirectoryService(LdapTemplate ldapTemplate) { this.ldapTemplate = ldapTemplate; }

    public Person lookupPerson(String username) {
        return (Person) ldapTemplate.lookup("cn=" + username, new PersonAttributesMapper());
    }

    public List<Person> searchDirectory(String searchterm) {
        SearchControls searchControls = new SearchControls();
        searchControls.setCountLimit(25);
        searchControls.setSearchScope(SearchControls.SUBTREE_SCOPE);

        List<Person> people = (List<Person>) ldapTemplate.search(
                BASE_DN, "cn=" + searchterm, searchControls, new PersonAttributesMapper());
        return people;
    }
}
Agreement answered 26/8, 2014 at 21:22 Comment(2)
Why? Why all the subclassing? Why not simply configure them in either xml or java config? SUbclassing for just configuration seems overkill to me...Arrowhead
Thanks for the suggestion, M! (That said, obviously the answer to "why" is b/c I'm learning and it worked, but that's why I put it out for review and suggestions for a better/cleaner way. A more friendly way to put it might have been, "For a better/cleaner approach, create a configuration class with @Configuration and methods that return the bean types you need using the Environment to get access to the Spring Boot properties instead of @Value." -- I'll try out the code and report back.Agreement
A
61

Why all the subclasses? Just use configuration to configure the beans. Either XML or Java Config.

@Configuration
public class LdapConfiguration {

    @Autowired
    Environment env;

    @Bean
    public LdapContextSource contextSource () {
        LdapContextSource contextSource= new LdapContextSource();
        contextSource.setUrl(env.getRequiredProperty("ldap.url"));
        contextSource.setBase(env.getRequiredProperty("ldap.base"));
        contextSource.setUserDn(env.getRequiredProperty("ldap.user"));
        contextSource.setPassword(env.getRequiredProperty("ldap.password"));
        return contextSource;
    }

    @Bean
    public LdapTemplate ldapTemplate() {
        return new LdapTemplate(contextSource());        
    }

}

Your DirectoryService can remain the same as it will have the LdapTemplate autowired.

A general rule of thumb is that you don't want to extend your infrastructure beans (like DataSource or LdapTemplate) but configure them explicitly. This as opposed to your application beans (services, repositories etc.).

Arrowhead answered 27/8, 2014 at 6:2 Comment(2)
Thanks again, M - your suggestion worked a treat, reduced LoC, and helped me better understand annotation based configuration style.Agreement
The autoconfiguration works only for localhost, but for remote server this is the solution.Middle
T
10

Explicit wiring up of your LDAP isn't necessary at all for straight forward cases. This is the sort of thing Spring Boot aims to eliminate by being opinionated in the first place.

Ensure you have the spring-boot-starter-data-ldap or the spring-ldap-core dependency included, e.g. for Maven in your pom:xml:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-ldap</artifactId>
</dependency>

Configure your LDAP in application.properties with the following keys:

# Note the spring prefix for each and use just the CN for username
spring.ldap.url=ldap://server.domain.com:389
spring.ldap.base=OU=Employees,OU=Users,DC=domain,DC=com
spring.ldap.username=myuserid
spring.ldap.password=secretthingy

Then simply rely on Spring to autowire, e.g. using field injection1:

@Autowired
private final LdapTemplate ldapTemplate;

Reference: Spring Boot Reference Guide: LDAP


1 Field injection is generally not recommended but it's used here for concision.

Thirtytwomo answered 26/11, 2018 at 2:51 Comment(3)
Great update to this Q from 2014. Lots of changes over the years. Next time I work with LDAP, will use this info. Thx!Agreement
This helped me today in my issue with regards to connecting with our LDAP server. Thank you so much for this!Cybele
Any idea how to load two LDAP server configuration using the above ?Deonnadeonne

© 2022 - 2025 — McMap. All rights reserved.