0

I need to use a list of objects for access control. The query to get that list is quite complex and lengthy, so I would like to cache it in the user object when a user authenticates with the server. I decided to try and solve this with a customised implementation of UserDetailService and this WebSecurityConfig:

@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

...


        @Override
        public void init(AuthenticationManagerBuilder auth) throws Exception {
            String ldapURI = "ldap://" + ldaHost + ":" + ldapPort + "/" + ldapBasis;

            DefaultSpringSecurityContextSource contextSource = new DefaultSpringSecurityContextSource(ldapURI);
            contextSource.setUserDn(ldapUser);
            contextSource.setPassword(ldapPassword);
            contextSource.afterPropertiesSet();

            LdapAuthenticationProviderConfigurer<AuthenticationManagerBuilder> ldapAuthenticationProviderConfigurer = auth
                    .ldapAuthentication().userDetailsContextMapper(new LdapUserDetailsContextMapper());

            ldapAuthenticationProviderConfigurer
                    .userSearchFilter("(&(criteria={0}))")
                    .userSearchBase("ou=usersearchbase").contextSource(contextSource);
        }
}

The ContextMapper to fill the user object with data:

public class LdapUserDetailsContextMapper implements UserDetailsContextMapper {

private final org.slf4j.Logger log = LoggerFactory.getLogger(this.getClass());

private DataService dataService;

public UserDetails mapUserFromContext(DirContextOperations ctx, String username,
                                      Collection<? extends GrantedAuthority> authorities) {

    AppUser user = new AppUser();
    user.setUsername(ctx.getStringAttribute("uid"));
    user.setName(ctx.getStringAttribute("cn"));
    user.setSurname(ctx.getStringAttribute("sn"));
    user.setMail(ctx.getStringAttribute("mail"));
    user.setRoleUser();    

user.setManaged(dataService.getManagedData(user.getMail()));

                    return user;
                }

The problem is that I see no way to inject my DataService into this process - neither the ContextMapper nor the @Configure class are managed beans which makes @Autowired impossible. I want to avoid LoadTimeWeaving because using this would make deployments very difficult - do I have another choice?

I found these two similar problems: Why is my Spring @Autowired field null? has the same problem in a Controller class, which can be turned into a managed bean; both other mentioned solutions didn't work for me (dataService always was null) and Spring Boot, @Autowire into an unmanaged class using @Configurable and load time weaving again recommends LoadTimeWeaving.

I also found apparent solutions mentioning using @Autowired at the init method, but I couldn't get get that to work either (trying to call the ContextMapper-constructor with an injected dataService)

Community
  • 1
  • 1
Torsten N.
  • 2,051
  • 1
  • 12
  • 17
  • 1
    Just make `LdapUserDetailsContextMapper`. instead of creating a new instance, put that logic into a `@Bean` annotated method and call that. You are overthinking things... – M. Deinum Jul 18 '16 at 10:22
  • Doesn't `@Bean` do the same as `@Service/@Component etc` in that it declares & creates a singleton? I thought this was the case so I could not inject the result into my WebSecurityConfig class – Torsten N. Jul 18 '16 at 10:33
  • 1
    And why not. There is no state in your mapper, that can be a singleton perfectly... Heck it is even a singleton as you declare it right now, but the only difference is it isn't spring managed. – M. Deinum Jul 18 '16 at 10:35
  • 1
    Next to that `@Configuration` class are also spring managed classes which means you can also auto wire into them. Instead of doing guesswork I suggest a read of the documentation . – M. Deinum Jul 18 '16 at 10:36

1 Answers1

1

Just make it a spring managed bean.

@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

...


        @Override
        public void init(AuthenticationManagerBuilder auth) throws Exception {
            String ldapURI = "ldap://" + ldaHost + ":" + ldapPort + "/" + ldapBasis;

            DefaultSpringSecurityContextSource contextSource = new DefaultSpringSecurityContextSource(ldapURI);
            contextSource.setUserDn(ldapUser);
            contextSource.setPassword(ldapPassword);
            contextSource.afterPropertiesSet();

            LdapAuthenticationProviderConfigurer<AuthenticationManagerBuilder> ldapAuthenticationProviderConfigurer = auth
                    .ldapAuthentication().userDetailsContextMapper(userDetailsContextMapper());

            ldapAuthenticationProviderConfigurer
                    .userSearchFilter("(&(criteria={0}))")
                    .userSearchBase("ou=usersearchbase").contextSource(contextSource);
        }
    @Bean
    public LdapUserDetailsContextMapper userDetailsContextMapper() {
        return new LdapUserDetailsContextMapper();
    }
}

And annotate your field inside the LdapUserDetailsContextMapper with @Autowired.

M. Deinum
  • 115,695
  • 22
  • 220
  • 224