Spring security 无法自动装配 UserDetailsS​​ervice

Posted

技术标签:

【中文标题】Spring security 无法自动装配 UserDetailsS​​ervice【英文标题】:Spring security Cant autowire UserDetailsService 【发布时间】:2016-03-29 15:10:52 【问题描述】:

我在尝试从数据库添加身份验证时卡住了。

这是错误日志:

2015 年 12 月 23 日 08:24:32.819 严重 [localhost-startStop-1] org.springframework.web.context.ContextLoader.initWebApplicationContext 上下文初始化失败 org.springframework.beans.factory.BeanCreationException:错误 创建名为“securityConfig”的bean:注入自动装配 依赖失败;嵌套异常是 org.springframework.beans.factory.BeanCreationException:不能 自动接线字段: org.springframework.security.core.userdetails.UserDetailsS​​ervice kamienica.configuration.SecurityConfig.userDetailsS​​ervice;嵌套的 例外是 org.springframework.beans.factory.NoSuchBeanDefinitionException: 否 符合条件的 bean 类型 [org.springframework.security.core.userdetails.UserDetailsS​​ervice] 找到依赖项:预计至少有 1 个符合条件的 bean 此依赖项的自动装配候选者。依赖注解: @org.springframework.beans.factory.annotation.Autowired(required=true), @org.springframework.beans.factory.annotation.Qualifier(value=customUserDetailsS​​ervice) 在 org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:292) 在 org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1185) 在 org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:537) 在 org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:475) 在 org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:302) 在 org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:228) 在 org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:298) 在 org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:193) 在 org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:703) 在 org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:760) 在 org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:482) 在 org.springframework.web.context.ContextLoader.configureAndRefreshWebApplicationContext(ContextLoader.java:403) 在 org.springframework.web.context.ContextLoader.initWebApplicationContext(ContextLoader.java:306) 在 org.springframework.web.context.ContextLoaderListener.contextInitialized(ContextLoaderListener.java:106) 在 org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:4727) 在 org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5167) 在 org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150) 在 org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:725) 在 org.apache.catalina.core.ContainerBase.addChild(ContainerBase.java:701) 在 org.apache.catalina.core.StandardHost.addChild(StandardHost.java:717) 在 org.apache.catalina.startup.HostConfig.deployDescriptor(HostConfig.java:586) 在 org.apache.catalina.startup.HostConfig$DeployDescriptor.run(HostConfig.java:1750) 在 java.util.concurrent.Executors$RunnableAdapter.call(未知 源)在 java.util.concurrent.FutureTask.run(未知源)在 java.util.concurrent.ThreadPoolExecutor.runWorker(未知来源)在 java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source) at java.lang.Thread.run(Unknown Source) 原因: org.springframework.beans.factory.BeanCreationException:不能 自动接线字段: org.springframework.security.core.userdetails.UserDetailsS​​ervice kamienica.configuration.SecurityConfig.userDetailsS​​ervice;嵌套的 例外是 org.springframework.beans.factory.NoSuchBeanDefinitionException: 否 符合条件的 bean 类型 [org.springframework.security.core.userdetails.UserDetailsS​​ervice] 找到依赖项:预计至少有 1 个符合条件的 bean 此依赖项的自动装配候选者。依赖注解: @org.springframework.beans.factory.annotation.Autowired(required=true), @org.springframework.beans.factory.annotation.Qualifier(value=customUserDetailsS​​ervice) 在 org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:508) 在 org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:87) 在 org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:289) ... 26 更多原因: org.springframework.beans.factory.NoSuchBeanDefinitionException: 否 符合条件的 bean 类型 [org.springframework.security.core.userdetails.UserDetailsS​​ervice] 找到依赖项:预计至少有 1 个符合条件的 bean 此依赖项的自动装配候选者。依赖注解: @org.springframework.beans.factory.annotation.Autowired(required=true), @org.springframework.beans.factory.annotation.Qualifier(value=customUserDetailsS​​ervice) 在 org.springframework.beans.factory.support.DefaultListableBeanFactory.raiseNoSuchBeanDefinitionException(DefaultListableBeanFactory.java:1103) 在 org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:963) 在 org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:858) 在 org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:480) ... 28 更多

还有我的配置类:

1) 应用配置。

@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) 应用初始化器:

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("/");

    


现在是最重要的部分:

@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")
        ;
    

我的自定义用户服务:

@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);
    


我在这里做错了什么?

编辑 1。 我已经修改了以下文件中的注释,但没有解决问题:

@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);
    


还有:

@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")
        ;
    

编辑 2:根据 Selva 的建议:

@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();
    

不幸的是,结果相同:

没有符合条件的 bean 类型 [kamienica.service.CustomUserDetailsS​​ervice] 找到依赖项: 预计至少有 1 个 bean 有资格作为 autowire 候选 这种依赖。依赖注解: @org.springframework.beans.factory.annotation.Autowired(required=true)

【问题讨论】:

请在 SecurityConfig 类中自动连接 CustomUserDetailsS​​ervice 类而不是 UserDetailsS​​ervice 类 @Selva 我照你说的做了,但没有阳性结果... 组件扫描似乎没有按预期工作。 我在 aapConfig(第 1 点)中有一个 componentScan,它设置为 kamienica。我所有的包都被命名为 kamienica.* 例如:kamienica.dao (etc) 你还记得你是怎么解决这个问题的吗? 【参考方案1】:
    @Component@ServiceCustomUserDetailsService 一起使用,不能同时使用。

    如果你使用的是@Service,那么使用它:

    @Service("userDetailsService")

    现在摆脱@Qualifier。只需使用:

    @Autowired UserDetailsService userDetailsService;

【讨论】:

【参考方案2】:

我是 Spring Boot 的初学者,我遇到了类似的问题,但是当我为 Spring 身份验证进行测试时就出现了。 我的情况:

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

@Autowired
private WebApplicationContext webApplicationContext;
private MockMvc mockMvc;

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

并收到:

org.springframework.beans.factory.UnsatisfiedDependencyException: 创建名为“some.package.myClass.DefaultUserAuthenticationTest”的 bean 时出错: 通过字段“webApplicationContext”表示的不满足的依赖关系; 嵌套异常是 org.springframework.beans.factory.NoSuchBeanDefinitionException: 没有可用的“org.springframework.web.context.WebApplicationContext”类型的合格bean: 预计至少有 1 个 bean 有资格作为 autowire 候选者。依赖注解: @org.springframework.beans.factory.annotation.Autowired(required=true)

我这样做的时候:

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

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

异常消失

我找到了解决方案here。

【讨论】:

【参考方案3】:

我也面临同样的问题,问题是我的一个安全包拼错了。所有软件包都是 com.apps.test 并且为了安全起见它是 com.app.test 并且它正在失败。 请检查您的所有包的初始值,因为在组件扫描期间,如果它不一样,它将失败。

【讨论】:

以上是关于Spring security 无法自动装配 UserDetailsS​​ervice的主要内容,如果未能解决你的问题,请参考以下文章

Spring Security:自动装配 ProviderManager

Spring Security:自动装配 ProviderManager

使用 Spring Security 的自动装配依赖注入

Spring Security WebSecurityConfigurerAdapter:AuthenticationManagerBuilder - 覆盖配置方法或自动装配 globalUserDe

Spring Security @PreAuthorize 使用 SpEL 语言访问自动装配的 bean [重复]

无法自动装配字段:私有 org.springframework.security.authentication.AuthenticationManager