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)
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
.