Custom UserDetailsService it seems to be not autow

2019-07-19 09:14发布

问题:

Well I've expended quite a time figuring out what the problem with my code is. According to the trace that tomcat is sending me it has something to do with a custom UserDetailsService that I create for connecting Spring Security and JPA to make a login system based on data in a Database. I've read several articles so far talking about this issue but I haven't found a solution for my one. So heres the error trace:

 ERROR: org.springframework.web.context.ContextLoader - Context initialization failed
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'springSecurityConfig': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: org.springframework.security.core.userdetails.UserDetailsService com.trabajotoo.saleisi.configuration.SpringSecurityConfig.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:334)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1214)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:543)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:482)
    at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:306)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:772)
    at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:839)
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:538)
    at org.springframework.web.context.ContextLoader.configureAndRefreshWebApplicationContext(ContextLoader.java:446)
    at org.springframework.web.context.ContextLoader.initWebApplicationContext(ContextLoader.java:328)
    at org.springframework.web.context.ContextLoaderListener.contextInitialized(ContextLoaderListener.java:107)
    at org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:4937)
    at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5434)
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
    at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1559)
    at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1549)
    at java.util.concurrent.FutureTask.run(FutureTask.java:262)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
    at java.lang.Thread.run(Thread.java:745)
Caused by: org.springframework.beans.factory.BeanCreationException: Could not autowire field: org.springframework.security.core.userdetails.UserDetailsService com.trabajotoo.saleisi.configuration.SpringSecurityConfig.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:573)
    at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:88)
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:331)
    ... 22 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:1373)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1119)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1014)
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:545)
    ... 24 more

Heres my CustomUserDetailsService:

package com.trabajotoo.saleisi.services;


import java.util.ArrayList;
import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;

import com.trabajotoo.saleisi.model.RolAsignado;
import com.trabajotoo.saleisi.model.Usuario;

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

    @Autowired
    private UsuarioService userService;

    @Transactional(readOnly = true)
    public UserDetails loadUserByUsername(String userName) throws 
    UsernameNotFoundException {
        Usuario user = userService.findByUsername(userName);
        if (user == null){
            System.out.println("User not found");
            throw new UsernameNotFoundException("Username not found");
        }

        return new User(user.getNomUsuario(), user.getPassword(), user.isEstado(),
                true, true, true, getGrantedAuthorities(user));
    }

    private List<GrantedAuthority> getGrantedAuthorities(Usuario user){
        List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
        for(RolAsignado rolAsign: user.getRolesAsignados()){
            if (rolAsign.isEstado()){
                authorities.add(new SimpleGrantedAuthority("ROLE_"+
            rolAsign.getRol().getCodigo()));
                break;
            }
        }

        return authorities;
    }


}

and here is my SprinSecurityConfig class:

package com.trabajotoo.saleisi.configuration;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

import com.trabajotoo.saleisi.services.CustomSuccessHandler;

@Configuration
@EnableWebSecurity
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter{


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

    @Autowired
    CustomSuccessHandler customSuccesHandler;



    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
        auth.authenticationProvider(authenticationProvider());
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Bean
    public DaoAuthenticationProvider authenticationProvider() {
        DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider();
        authenticationProvider.setUserDetailsService(userDetailsService);
        authenticationProvider.setPasswordEncoder(passwordEncoder());
        return authenticationProvider;
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception{
        http.authorizeRequests()
        .antMatchers("/register").permitAll()
        .antMatchers("/admin/**").access("hasRole('ADMIN')")
        .antMatchers("/coordinador/**").access("hasRole('COORD')")
        .antMatchers("/director/**").access("hasRole('DIR')")
        .and().formLogin().loginPage("/").permitAll()
        .successHandler(customSuccesHandler).usernameParameter("nomUsuario")
        .passwordParameter("password");
    }

}

Here's my SpringSecurityWebApplicationInitializer:

package com.trabajotoo.saleisi.configuration;
import org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer;

public class SecurityWebApplicationInitializer extends 
AbstractSecurityWebApplicationInitializer{

}

My WebConfiguration class:

package com.trabajotoo.saleisi.configuration;

import javax.annotation.Resource;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.core.env.Environment;
import org.springframework.web.servlet.ViewResolver;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
import org.springframework.web.servlet.view.JstlView;

@Configuration
@EnableWebMvc
@Import({HibernateConfig.class})
@ComponentScan({"com.trabajotoo.saleisi.controllers", "com.trabajotoo.saleisi.dao", 
    "com.trabajotoo.saleisi.services"})
public class WebConfiguration extends WebMvcConfigurerAdapter{

    @Resource
    private Environment env;

    // Configuracion de los archivos css,js,etc.
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry){
        registry.addResourceHandler("/resources/**")
        .addResourceLocations("/resources/");
    }

    // Configuracion del manejo de las vistas
    @Bean
    public ViewResolver viewResolver() {
        InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
        viewResolver.setViewClass(JstlView.class);
        viewResolver.setPrefix("/WEB-INF/views/");
        viewResolver.setSuffix(".jsp");

        return viewResolver;
    }

}

and My WebAppInitializer:

package com.trabajotoo.saleisi.configuration;

import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;

import com.trabajotoo.saleisi.configuration.RootContext;
import com.trabajotoo.saleisi.configuration.WebConfiguration;

public class ApplicationInitializer extends
AbstractAnnotationConfigDispatcherServletInitializer {

    @Override
    protected Class<?>[] getRootConfigClasses() {
        return new Class[] { RootContext.class, SpringSecurityConfig.class };
    }

    @Override
    protected Class<?>[] getServletConfigClasses() {
        return new Class[] { WebConfiguration.class };
    }

    @Override
    protected String[] getServletMappings() {
        return new String[] { "/" };
    }

}

By the way I'm using Spring Security 4.0.3 and Spring MVC 4.2.4

回答1:

You're the victim of Application Context Hierarchies. Your CustomUserDetailsService is picked up by WebConfiguration component scanning and defined in this context, since:

@ComponentScan({"com.trabajotoo.saleisi.controllers", "com.trabajotoo.saleisi.dao", 
    "com.trabajotoo.saleisi.services"})
public class WebConfiguration extends WebMvcConfigurerAdapter { ... }

Then you're gonna inject this bean into SpringSecurityConfig, which is WebConfiguration's parent context. Since SpringSecurityConfig can not access to the beans defined in its child context, it will throw a NoSuchBeanDefinitionException.

In order to solve this problem, you should define CustomUserDetailsService in root context. I'm not quite familiar with your project structure but scanning components in the service layer by RootContext or SpringSecurityConfig may fix your problem. Something like this:

@Configuration
@ComponentScan("com.trabajotoo.saleisi.services")
public class RooContext { ... }


回答2:

Try adding below in WebSecurityConfiguration.java

@Bean
public UserDetailsService userDetailsService() {
    return super.userDetailsService();
}