Spring security Cant autowire UserDetailsService

2020-06-23 09:04发布

问题:

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

回答1:

  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;



回答2:

I am beginner in Spring Boot and I met a similar problem but it is have been when i make tests for spring authentication. My case:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
public class DefaultUserAuthenticationTest {

@Autowired
private WebApplicationContext webApplicationContext;
private MockMvc mockMvc;

@Before
public void setUp() {
        mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext)
                  .build();
...

and received:

org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'some.package.myClass.DefaultUserAuthenticationTest': Unsatisfied dependency expressed through field 'webApplicationContext'; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'org.springframework.web.context.WebApplicationContext' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}

When i did that:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
@WebAppConfiguration  //added this annotation
public class DefaultUserAuthenticationTest {

@Autowired
private WebApplicationContext webApplicationContext;
private MockMvc mockMvc;
...

exception disappeared

I finded solution here.



回答3:

I am also facing the same issue and issue was that one of my security package my misspelled. all package was com.apps.test and for security it was com.app.test and it was getting failed. please check all your package initial as during component scanning it will fail if it i not same.