0

I am implementing ldap authentication using Spring Security. It works when I hardcode all the ldap server information in following configuration class.

//WebSecurityConfig.java
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .authorizeRequests()
                .anyRequest()
                .authenticated()
                .and()
            .formLogin();
    }

    @Configuration
    protected static class AuthenticationConfiguration extends
            GlobalAuthenticationConfigurerAdapter {

        @Override
        public void init(AuthenticationManagerBuilder auth) throws Exception { 

            DefaultSpringSecurityContextSource contextSource = new DefaultSpringSecurityContextSource("ldap://ldap.mdanderson.edu:389/dc=mdanderson,dc=edu");
            contextSource.setUserDn("cn=ris_flow,ou=service accounts,ou=institution,ou=service accounts,dc=mdanderson,dc=edu");
            contextSource.setPassword("xxxyyyzzz");
            contextSource.setReferral("follow"); 
            contextSource.afterPropertiesSet();  
            LdapAuthenticationProviderConfigurer<AuthenticationManagerBuilder> ldapAuthenticationProviderConfigurer = auth.ldapAuthentication();

            ldapAuthenticationProviderConfigurer
                .userDnPatterns("cn={0},ou=institution,ou=people")
                .userSearchBase("")
                .contextSource(contextSource);
        }
    }
}

I decided to put these server information in application.properties and set the variables using @Value in my config class, so I add the following right before AuthenticationConfiguration.

@Value("${ldap.contextSource.url")
private static String url;

@Value("${ldap.contextSource.managerDn")
private static String userDn;

@Value("${ldap.contextSource.managerPass")
private static String userPass;

And replaced the lines of contextSource to:

    DefaultSpringSecurityContextSource contextSource = new DefaultSpringSecurityContextSource(url);
    contextSource.setUserDn(userDn);
    contextSource.setPassword(userPass);

However when I ran it again, the application failed to start with errors below:

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'springSecurityFilterChain' defined in class path resource.......
Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate....
Caused by: java.lang.IllegalArgumentException: An LDAP connection URL must be supplied.


org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'springSecurityFilterChain' defined in class path resource....
Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate....
Caused by: org.springframework.security.config.annotation.AlreadyBuiltException: This object has already been built

What did I do wrong?

Jorge
  • 1,136
  • 9
  • 15
Nasreddin
  • 1,509
  • 9
  • 31
  • 36

1 Answers1

1

Check this piece of code

    @Value("${ldap.contextSource.url") 
private static String url; 
    @Value("${ldap.contextSource.managerDn") 
private static String userDn; 
    @Value("${ldap.contextSource.managerPass") 
private static String userPass;

You need to close the brackets properly this way

@Value("${ldap.contextSource.url}") private static String url; 
@Value("${ldap.contextSource.managerDn}") private static String userDn; 
@Value("${ldap.contextSource.managerPass}") private static String userPass;

From Spring In Action Fourth Edition book:

When relying on component-scanning and autowiring to create and initialize your application components, there’s no configuration file or class where you can specify the placeholders. Instead, you can use the @Value annotation in much the same way as you might use the @Autowired annotation. In order to use placeholder values, you must configure either a PropertyPlaceholderConfigurer bean or a PropertySourcesPlaceholderConfigurer bean. Starting with Spring 3.1, PropertySourcesPlaceholderConfigurer is preferred because it resolves placeholders against the Spring Environment and its set of property sources. The following @Bean method configures PropertySourcesPlaceholderConfigurer in Java configuration:

@Bean
public
static PropertySourcesPlaceholderConfigurer placeholderConfigurer() {
    return new PropertySourcesPlaceholderConfigurer();
}

EDIT: Complete example accesing properties using SPRING 4.2.5 RELEASE

Configuration Class:

@Configuration
@ComponentScan
@PropertySource("classpath:/your/package/example.properties")
// In my case, this package is stored in src/main/resources folder, which is in the classpath of the application
public class SpringPropertiesConfig {

    @Bean
    public static PropertySourcesPlaceholderConfigurer placeholderConfigurer() {
        return new PropertySourcesPlaceholderConfigurer();
    }
}

Component (Bean) accessing the properties:

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

@Component
public class ComponentAccessingProperties {

    @Value("${first.property}")
    private String propertyOne;

    @Value("${second.property}")
    private String propertyTwo;


    public String getPropertyOne() {
        return propertyOne;
    }

    public String getPropertyTwo() {
        return propertyTwo;
    }

}

Example properties file (/your/package/example.properties):

first.property=ONE
second.property=SECOND

Test Class:

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import your.package.ComponentAccessingProperties;
import your.package.SpringPropertiesConfig;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SpringPropertiesConfig.class)
public class TestAccessingProperties {

    @Autowired
    private ComponentAccessingProperties componentAccesingProperties;

    @Test
    public void shouldNotBeNull() {
        assertNotNull(componentAccesingProperties);
    }

    @Test
    public void checkProperties() {
        assertEquals("ONE", componentAccesingProperties.getPropertyOne());
        assertEquals("SECOND", componentAccesingProperties.getPropertyTwo());
    }
}
Jorge
  • 1,136
  • 9
  • 15
  • unfortunately still the same – Nasreddin Apr 20 '16 at 21:42
  • Ok, checking your code, in your configuration class your should annotate the class with @PropertySource to indicate where properties are located and use a bean of class PropertiesPlaceholderConfigurer. Check this link where you can find an example http://websystique.com/spring/spring-propertysource-value-annotations-example/. I didn't try it but seems to be fine – Jorge Apr 21 '16 at 05:15
  • I am getting the error `FileNotFoundException: class path resource [application.properties] cannot be opened because it does not exist`. I have my `application.properties` under /src/main/resources/config. I think that is in classpath with auto config of Spring Boot. I have db connection information in that file which was apparently picked up correctly. – Nasreddin Apr 21 '16 at 15:49
  • I understand that "/src/main/resources" should be the classpath main resources folder, so your application.properties would be in config/application.properties. Try to set this path "@PropertySource(value = { "classpath:config/application.properties" })" and tell me ok? – Jorge Apr 21 '16 at 15:53
  • You have used $ or # in your value annotation? – Jorge Apr 21 '16 at 16:02
  • Make sure that your properties file contains this entry "ldap.contextSource.url" matching case and also try to set the Annotation this way "@Value("${ldap.contextSource.url:any_default_value_for_url") " and check if at least, it sets correctly the default value – Jorge Apr 21 '16 at 16:10
  • Ok, set the initial bar as well in your PropertySource annotation ""@PropertySource(value = { "classpath:/config/application.properties" })"" – Jorge Apr 21 '16 at 16:14
  • Tried both, still the same – Nasreddin Apr 21 '16 at 19:37
  • I don't know exactly what's happening. I have edit my answer including a complete fully working example of autowiring properties. Please check it and compare with yours to see if you have something missing. Hope it helps... – Jorge Apr 22 '16 at 09:11
  • I know whats happening I think. You're declaring your variables as 'static', and Spring would tell you something like this: "Autowired annotation is not supported on static fields". Remove that "static" keyword and test again... – Jorge Apr 22 '16 at 09:36
  • @Nasreddin Did you get this with my last two comments? – Jorge Apr 25 '16 at 14:27
  • I tried everything you suggested but it still doesn't work, same error. – Nasreddin Apr 26 '16 at 02:52
  • And if I remove `static` for say `url`, I will get `Cannot make a static reference to the non-static field` error – Nasreddin Apr 26 '16 at 14:30