8
votes

What customization's are required in order to secure a Reactive Spring Boot application with LDAP? The examples I've seen so far are based on Spring MVC and the example for securing a WebFlux only shows a simple Reactive example with an in-memory Map.

2

2 Answers

8
votes

Here is one solution for this that I have come up with and tested.

Deserving special attention is this information in this class: ReactiveAuthenticationManagerAdapter. There, it states:

Adapts an AuthenticationManager to the reactive APIs. This is somewhat necessary because many of the ways that credentials are stored (i.e. JDBC, LDAP, etc) do not have reactive implementations. What's more is it is generally considered best practice to store passwords in a hash that is intentionally slow which would block ever request from coming in unless it was put on another thread.

First, create a configuration class. This will handle the connectivity to LDAP.

@Configuration
public class ReactiveLdapAuthenticationConfig {

    // Set this in your application.properties, or hardcode if you want.
    @Value("${spring.ldap.urls}")
    private String ldapUrl;

    @Bean
    ReactiveAuthenticationManager authenticationManager(BaseLdapPathContextSource contextSource) {

        BindAuthenticator ba = new BindAuthenticator(contextSource);
        ba.setUserDnPatterns(new String[] { "cn={0},ou=people" } );

        LdapAuthenticationProvider lap = new LdapAuthenticationProvider(ba);

        AuthenticationManager am = new ProviderManager(Arrays.asList(lap));

        return new ReactiveAuthenticationManagerAdapter(am);

    }

    @Bean
    BaseLdapPathContextSource contextSource() {
        LdapContextSource ctx = new LdapContextSource();
        ctx.setUrl(ldapUrl);
        ctx.afterPropertiesSet();
        return ctx;
    }

}

After that, you'll want to configure your security following the patterns here. The most basic chain configuration is about this:

@Bean
public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
    http
        .authorizeExchange()
            .anyExchange().authenticated()
            .and()
        .httpBasic();

    return http.build();
}

For completeness, you'll want to make sure you have these:

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

Other References

4
votes

The above example didn't work for me using Windows Active Directory. I could get LDAP Authentication to work in stand-alone (non-Spring) Java, but the above solution always gave me error 52e (user known, but invalid password).

Following on from the example above, I used the same pom.xml and @EnableWebFluxSecurity ... SecurityWebFilterChain(...), but with the following;

@Configuration
public class ReactiveLdapAuthenticatoinConfig {

@Bean
ReactiveAuthenticationManager authenticationManager() {

   ActiveDirectoryLdapAuthenticationProvider adlap = 
      new ActiveDirectoryLdapAuthenticationProvider(
         "{my.domain}",
         "ldap://{my.ldap.server}.{my.domain}"
      );

   AuthenticationManager am = new ProviderManager(Arrays.asList(adlap));
   return new ReactiveAuthenticationManagerAdapter(am);
}
}

In order to return the signed-in user, one would use something like;

@GetMapping(value = '/user')
public Mono<String> getUser(Mono<Principal> principal) {
   return principal.map(Principal::getName);
}