Java – Spring security Cant autowire UserDetailsService

hibernatejavaspringspring-mvcspring-security

I got stuck trying to add authentication from database.

Here is error log:

23-Dec-2015 08:24:32.819 SEVERE [localhost-startStop-1]
org.springframework.web.context.ContextLoader.initWebApplicationContext
Context initialization failed
org.springframework.beans.factory.BeanCreationException: Error
creating bean with name 'securityConfig': Injection of autowired
dependencies failed; nested exception is
org.springframework.beans.factory.BeanCreationException: Could not
autowire field:
org.springframework.security.core.userdetails.UserDetailsService
kamienica.configuration.SecurityConfig.userDetailsService; nested
exception is
org.springframework.beans.factory.NoSuchBeanDefinitionException: No
qualifying bean of type
[org.springframework.security.core.userdetails.UserDetailsService]
found for dependency: expected at least 1 bean which qualifies as
autowire candidate for this dependency. Dependency annotations:
{@org.springframework.beans.factory.annotation.Autowired(required=true),
@org.springframework.beans.factory.annotation.Qualifier(value=customUserDetailsService)}
at
org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:292)
at
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1185)
at
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:537)
at
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:475)
at
org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:302)
at
org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:228)
at
org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:298)
at
org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:193)
at
org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:703)
at
org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:760)
at
org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:482)
at
org.springframework.web.context.ContextLoader.configureAndRefreshWebApplicationContext(ContextLoader.java:403)
at
org.springframework.web.context.ContextLoader.initWebApplicationContext(ContextLoader.java:306)
at
org.springframework.web.context.ContextLoaderListener.contextInitialized(ContextLoaderListener.java:106)
at
org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:4727)
at
org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5167)
at
org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
at
org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:725)
at
org.apache.catalina.core.ContainerBase.addChild(ContainerBase.java:701)
at
org.apache.catalina.core.StandardHost.addChild(StandardHost.java:717)
at
org.apache.catalina.startup.HostConfig.deployDescriptor(HostConfig.java:586)
at
org.apache.catalina.startup.HostConfig$DeployDescriptor.run(HostConfig.java:1750)
at java.util.concurrent.Executors$RunnableAdapter.call(Unknown
Source) at java.util.concurrent.FutureTask.run(Unknown Source) at
java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source) at
java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source) at
java.lang.Thread.run(Unknown Source) Caused by:
org.springframework.beans.factory.BeanCreationException: Could not
autowire field:
org.springframework.security.core.userdetails.UserDetailsService
kamienica.configuration.SecurityConfig.userDetailsService; nested
exception is
org.springframework.beans.factory.NoSuchBeanDefinitionException: No
qualifying bean of type
[org.springframework.security.core.userdetails.UserDetailsService]
found for dependency: expected at least 1 bean which qualifies as
autowire candidate for this dependency. Dependency annotations:
{@org.springframework.beans.factory.annotation.Autowired(required=true),
@org.springframework.beans.factory.annotation.Qualifier(value=customUserDetailsService)}
at
org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:508)
at
org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:87)
at
org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:289)
… 26 more Caused by:
org.springframework.beans.factory.NoSuchBeanDefinitionException: No
qualifying bean of type
[org.springframework.security.core.userdetails.UserDetailsService]
found for dependency: expected at least 1 bean which qualifies as
autowire candidate for this dependency. Dependency annotations:
{@org.springframework.beans.factory.annotation.Autowired(required=true),
@org.springframework.beans.factory.annotation.Qualifier(value=customUserDetailsService)}
at
org.springframework.beans.factory.support.DefaultListableBeanFactory.raiseNoSuchBeanDefinitionException(DefaultListableBeanFactory.java:1103)
at
org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:963)
at
org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:858)
at
org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:480)
… 28 more

And my configuration classes:

1) AppConfig.

@Configuration
@EnableWebMvc
@ComponentScan(basePackages = "kamienica")
public class AppConfig {

    @Bean
    public ViewResolver viewResolver() {
        InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
        viewResolver.setViewClass(JstlView.class);
        viewResolver.setPrefix("/WEB-INF/views/");
        viewResolver.setSuffix(".jsp");
        viewResolver.setContentType("UTF-8");
        return viewResolver;
    }

    @Bean
    public MessageSource messageSource() {
        ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
        messageSource.setBasename("messages");
        return messageSource;
    }

}

2) AppInitializer:

public class AppInitializer implements WebApplicationInitializer {

    public void onStartup(ServletContext container) throws ServletException {

        AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();
        ctx.register(AppConfig.class);
        ctx.setServletContext(container);

        ServletRegistration.Dynamic servlet = container.addServlet("dispatcher", new DispatcherServlet(ctx));

        // added to handle local characters
        FilterRegistration.Dynamic fr = container.addFilter("encodingFilter", new CharacterEncodingFilter());
        fr.setInitParameter("encoding", "UTF-8");
        fr.setInitParameter("forceEncoding", "true");
        fr.addMappingForUrlPatterns(null, true, "/*");

        servlet.setLoadOnStartup(1);
        servlet.addMapping("/");

    }

}

And now the most important part:

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    @Qualifier("customUserDetailsService")
    UserDetailsService userDetailsService;

    //
    // @Autowired
    // CustomSuccessHandler customSuccessHandler;
    //
    @Autowired
    public void configureGlobalSecurity(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService);
        auth.inMemoryAuthentication().withUser("admin").password("admin").roles("ADMIN");
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests().antMatchers("/", "/index").permitAll().antMatchers("/Admin/**")
                .access("hasRole('ADMIN')").antMatchers("/User/**").access("hasRole('ADMIN') or hasRole('USER')")
                // .and().formLogin().loginPage("/login")
                .and().formLogin()
                // .loginPage("/login")
                // .successHandler(customSuccessHandler)
                // .usernameParameter("email").passwordParameter("password")
                // .and().csrf()
                // .and().exceptionHandling().accessDeniedPage("/Access_Denied")
        ;
    }
}

My custom user service:

@Component
@Service("customUserDetailsService")
public class CustomUserDetailsService implements UserDetailsService {

    @Autowired
    TenantService tenantService;

    @Transactional(readOnly = true)
    public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException {

        Tenant tenant = tenantService.loadByMail(email);

        List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
        authorities.add(new SimpleGrantedAuthority(tenant.getRole()));
        return new org.springframework.security.core.userdetails.User(tenant.getEmail(), tenant.getPassword(), true,
                true, true, true, authorities);
    }

}

What am I doing wrong here?

EDIT 1.
I have modified the annotations in the files below but it did not solve the issue:

@Service("userDetailsService")
public class CustomUserDetailsService implements UserDetailsService {

    @Autowired
    TenantService tenantService;

    @Transactional(readOnly = true)
    public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException {

        Tenant tenant = tenantService.loadByMail(email);

        List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
        authorities.add(new SimpleGrantedAuthority(tenant.getRole()));
        return new org.springframework.security.core.userdetails.User(tenant.getEmail(), tenant.getPassword(), true,
                true, true, true, authorities);
    }

}

And:

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    UserDetailsService userDetailsService;

    //
    // @Autowired
    // CustomSuccessHandler customSuccessHandler;
    //
    @Autowired
    public void configureGlobalSecurity(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService);
        auth.inMemoryAuthentication().withUser("admin").password("admin").roles("ADMIN");
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests().antMatchers("/", "/index").permitAll().antMatchers("/Admin/**")
                .access("hasRole('ADMIN')").antMatchers("/User/**").access("hasRole('ADMIN') or hasRole('USER')")
                // .and().formLogin().loginPage("/login")
                .and().formLogin()
                // .loginPage("/login")
                // .successHandler(customSuccessHandler)
                // .usernameParameter("email").passwordParameter("password")
                // .and().csrf()
                // .and().exceptionHandling().accessDeniedPage("/Access_Denied")
        ;
    }
}

EDIT 2: As per Selva's suggestion:

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    CustomUserDetailsService customUserDetailsService;

    @Autowired
    public void configureGlobalSecurity(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(customUserDetailsService);
        auth.inMemoryAuthentication().withUser("admin").password("admin").roles("ADMIN");
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests().antMatchers("/", "/index").permitAll().antMatchers("/Admin/**")
                .access("hasRole('ADMIN')").antMatchers("/User/**").access("hasRole('ADMIN') or hasRole('USER')")
                .and().formLogin();
    }
}

Unfortunately same result:

No qualifying bean of type
[kamienica.service.CustomUserDetailsService] found for dependency:
expected at least 1 bean which qualifies as autowire candidate for
this dependency. Dependency annotations:
{@org.springframework.beans.factory.annotation.Autowired(required=true)}

Best Answer

  1. Either use @Component or @Service with CustomUserDetailsService, not both.
  2. If you are using @Service, then use it like:

    @Service("userDetailsService")

  3. Now get rid of the @Qualifier. Simply use:

    @Autowired UserDetailsService userDetailsService;

Related Topic