Torsten N.
Torsten N.

Reputation: 2141

Customizing UserDetails using a service without LoadTimeWeaving

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)

Upvotes: 0

Views: 394

Answers (1)

M. Deinum
M. Deinum

Reputation: 124471

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.

Upvotes: 1

Related Questions