【问题标题】:Could not autowire field: private org.springframework.security.core.userdetails.UserDetailsService无法自动装配字段:私有 org.springframework.security.core.userdetails.UserDetailsS​​ervice
【发布时间】:2014-02-26 20:48:01
【问题描述】:

我是 Spring 的新手,所以我一直在处理安全方面的问题。 每次我运行我的应用程序时,我都会得到:

org.springframework.beans.factory.BeanCreationException:创建名为“securityConfig”的bean时出错:注入自动装配的依赖项失败;嵌套异常是 org.springframework.beans.factory.BeanCreationException:无法自动装配字段:私有 org.springframework.security.core.userdetails.UserDetailsS​​ervice com.entirety.app.config.SecurityConfig.userDetailsS​​erviceImplementation;嵌套异常是 org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type of [org.springframework.security.core.userdetails.UserDetailsS​​ervice] found for dependency: 预计至少有 1 个 bean 有资格作为此依赖项的自动装配候选者。依赖注解:{@org.springframework.beans.factory.annotation.Autowired(required=true)}

我仔细梳理了我的代码,但无法查明问题所在。

Spring 框架版本:3.2.5.RELEASE Spring Security 版本:3.2.0.M2

SecurityConfig.java

package com.entirety.app.config;

import com.entirety.app.service.implement.UserDetailsServiceImplementation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
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 javax.sql.DataSource;

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled=true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    private DataSource dataSource;

    @Autowired
    private UserDetailsService userDetailsServiceImplementation;

    @Override
    protected void registerAuthentication(AuthenticationManagerBuilder auth) throws Exception {
        auth.jdbcAuthentication().dataSource(dataSource);
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.userDetailsService(userDetailsServiceImplementation)
                .authorizeUrls()
                    .antMatchers("/admin/**").hasRole("ADMIN")
                    .antMatchers("/sec/**").hasRole("MODERATOR")
                    .antMatchers("/*").permitAll()
                    .anyRequest().anonymous().and().exceptionHandling().accessDeniedPage("/denied").and()
                .formLogin()
                    .loginProcessingUrl("/j_spring_security_check")
                    .loginPage("/login")
                    .failureUrl("/error-login")
                .and()
                .logout()
                    .logoutUrl("/j_spring_security_logout")
                    .logoutSuccessUrl("/");
    }

    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }
}

CrmUserService.java

@Service("userService")
@Transactional
public class CrmUserService implements UserDetailsService {
    @Autowired
    private UserDAO userDAO;

    @Override
    public UserDetails loadUserByUsername(String login) throws UsernameNotFoundException {

        com.entirety.app.domain.User domainUser = userDAO.getUser(login);

        boolean enabled = true;
        boolean accountNonExpired = true;
        boolean credentialsNonExpired = true;
        boolean accountNonLocked = true;

        return new User(
                domainUser.getLogin(),
                domainUser.getPassword(),
                enabled,
                accountNonExpired,
                credentialsNonExpired,
                accountNonLocked,
                getAuthorities(domainUser.getAuthority().getId())
        );
    }

    public Collection<? extends GrantedAuthority> getAuthorities(Integer role) {
        List<GrantedAuthority> authList = getGrantedAuthorities(getRoles(role));
        return authList;
    }

    public List<String> getRoles(Integer role) {

        List<String> roles = new ArrayList<String>();

        if (role.intValue() == 1) {
            roles.add("ROLE_MODERATOR");
            roles.add("ROLE_ADMIN");
        } else if (role.intValue() == 2) {
            roles.add("ROLE_MODERATOR");
        }
        return roles;
    }

    public static List<GrantedAuthority> getGrantedAuthorities(List<String> roles) {
        List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();

        for (String role : roles) {
            authorities.add(new SimpleGrantedAuthority(role));
        }
        return authorities;
    }
}

没有合理的理由说明它应该失败但它确实失败了。

注意:

我的 IDE 可以链接自动装配(也就是说,它知道自动装配的来源以及所有内容)但编译失败。

编辑 2: 我从 SecurityConfig.java 文件中删除了以下代码

@Autowired
private UserDataService userDataService;

并将其添加到我知道定期调用的 POJO 和控制器之一并包含其他服务。 在这种情况下,将该代码粘贴到我的 baseController.java 文件中,该文件用于渲染主页。 该服务已正确编译,我什至可以在控制器中调用它。

所以问题只存在于SecurityConfig.java等配置文件中,这是唯一一次不起作用。

编辑 3:添加额外文件

SecurityInitializer.java

@Order(2)
public class SecurityInitializer extends AbstractSecurityWebApplicationInitializer  {
}

WebInitializer.java

@Order(1)
public class WebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
    @Override
    protected Class<?>[] getRootConfigClasses() {
        return new Class<?>[] { PersistanceConfig.class, SecurityConfig.class };  //To change body of implemented methods use File | Settings | File Templates.
    }

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

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

//    @Override
//    protected Filter[] getServletFilters() {
//        CharacterEncodingFilter characterEncodingFilter = new CharacterEncodingFilter();
//        characterEncodingFilter.setEncoding("UTF-8");
//        return new Filter[] { characterEncodingFilter};
//    }
}

mvc-dispatcher-servlet.xml

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

    <context:component-scan base-package="com.entirety.app"/>

    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/pages/"/>
        <property name="suffix" value=".jsp"/>
    </bean>

</beans>

web.xml

<web-app version="2.4"
    xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee 
    http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">

    <display-name>Spring MVC Application</display-name>

    <servlet>
        <servlet-name>mvc-dispatcher</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>

    <servlet-mapping>
        <servlet-name>mvc-dispatcher</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
</web-app>

【问题讨论】:

  • 是否扫描了 UserDetailsS​​erviceImplementation?
  • 你的UserDetailsServiceImplementation bean 定义在哪里?
  • 我怀疑您忘记对相应的包进行组件扫描。
  • 我在同一个文件夹中有 6 个其他服务,所有这些服务都在被扫描和使用,没有问题。因此可以安全地假设我检查的第一件事是 a) 文件夹是组件扫描的目标 b) userDetailsS​​erviceIMplementation 中没有问题
  • UserDetailsService 类,在同一个包中的CrmUserServiceSecurityConfig 中(UserDetailsService 是一个通用名称,您可能从两个不同的包中导入它们。)跨度>

标签: java spring spring-security autowired


【解决方案1】:

更新(提供完整示例后)

您似乎有多个问题。

web.xml 与 AbstractAnnotationConfigDispatcherServletInitializer

您创建的应用程序有一个 web.xml,它配置了一个名为 mvc-dispatcher 的 DispatcherServlet。 mvc-dispatcher 的配置为mvc-dispatcher-servlet.xml,它加载了包com.springapp.sectest 中的所有bean。这意味着mvc-dispatcher 可以找到您的UserDetailsService

该应用程序还有一个 AbstractAnnotationConfigDispatcherServletInitializer,它创建了一个 DispatcherServlet 来加载您的 WebConfig java 配置。这个DispatcherServlet 看不到mvc-dispatcher 创建的任何Bean。

简而言之,您应该使用 web.xml 或 AbstractAnnotationConfigDispatcherServletInitializer 配置 DispatcherServlet,而不是两者。

getRootConfigClasses 与 getServletConfigClasses

getRootConfigClasses 中的任何配置通常称为根配置或父配置,并且无法查看在 getServletConfigClasses 中定义的 bean。 Spring Security 使用由 @EnableWebSecurity 注解创建的名为 springSecurityFilterChain 的过滤器来保护您在 getRootConfigClasses 中定义的应用程序。这意味着通常最好将 Spring Security 的配置放在 getRootConfigClasses 中。有一些例外情况,比如如果你想在 Spring MVC 控制器上做方法安全。

getServletConfigClasses 中的任何配置通常称为子配置,并且可以查看在 getRootConfigClasses 中定义的 bean。 getServletConfigClasses 配置 DispatcherServlet 并且必须包含 Spring MVC 的 bean(即控制器、ViewResovlers 等)。 getRootConfigClasses 中定义的任何 Spring MVC bean 都是可见的,但未使用。

此设置虽然有点令人困惑,但允许您拥有多个具有隔离配置的 DispatcherServlet 实例。但实际上,很少有多个 DispatcherServlet 实例。

鉴于以上信息,您应该将UserDetailsService 声明及其所有依赖bean 移至getRootConfigClasses。这将确保 SecurityConfig.java 可以找到UserDetailsService

拉取修复请求

我已提交 PR 以获取您的申请,以便它开始时没有错误 https://github.com/worldcombined/AnnotateFailed/pull/1 。一些注意事项

  • 测试不使用父子上下文,因此它不反映正在运行的应用程序
  • 我不得不移动资源文件夹以便正确拾取它
  • 我没有这样做,所以您可以实际进行身份验证。没有 login.jsp 并且您提供的 UserDetailsS​​ervice 始终返回 null 相反,我试图证明此处的错误已得到解答。

原答案

基于此评论:

@ComponentScan 在我的 mvc-dispatcher-servlet.xml 中作为

您似乎是在 Dispatcher 配置中配置服务,而不是在根上下文中。

Spring Security 的配置通常在根上下文中配置。例如,可以如下所示扩展 AbstractSecurityWebApplicationInitializer:

import org.springframework.security.web.context.*;

public class SecurityWebApplicationInitializer
      extends AbstractSecurityWebApplicationInitializer {
}

您如何导入安全配置?如果 Spring Security 配置在根上下文中,那么它将看不到在 Dispatcher Servlet 上下文中定义的 bean。

解决方案是将服务的配置移动到根上下文中或将 Spring Security 配置移动到调度程序的 ApplicationContext。例如,如果您的调度程序 servlet 名为 mvc,则将 Spring Security 配置移动到调度程序上下文将如下所示。

public class SecurityWebApplicationInitializer
      extends AbstractSecurityWebApplicationInitializer {
    protected String getDispatcherWebApplicationContextSuffix() {
        return "mvc";
    }
}

【讨论】:

  • 是的,这一切都已经解决了。正如在其中一个 cmets 中提到的,我对此应用程序使用注释而不是 XML。所以一切都是按照注释方法设置的。例如要构建我拥有(或接近)的结构,您可以看到 Spring 拥有的 YummyNoddleBar 演示。我有同样的结构。除了我正在使用客户用户服务。
  • 我想我错过了你说一切正常的评论。如果您确实解决了问题,您可能应该将答案标记为已接受。
  • 抱歉 Rob,这与您的解决方案有关。我已经这样实现了,如果我使用基本身份验证(例如 inMemoryAuthentication 或基本 jdbcAuthentication),它工作正常。但是当我开始有点花哨并有一个自定义的 UserDetails 时,它就会分崩离析。我已经尝试重新创建整个项目,只关注 UserDetails 并且仍然是同样的问题。因此为什么它让我发疯:)
  • 请包括您配置 DispatcherServlet 的方式和根应用程序上下文。如果您使用的是 web.xml,这将是您的 web.xml。如果您使用的是 Java 配置,那么它将是您的 WebApplicationInitializer 类。还请包括用于加载(执行类路径扫描)UserDetailsS​​ervice 的类。
  • 嘿 Rob,我已经包含了所有主要的配置文件,包括 web.xml 和 dispatcher servlet.xml
【解决方案2】:

对 SecurityConfig 类使用@组件

【讨论】:

  • 这就是为什么帮助比@Configuration 更好?
  • @Component 已过时,因为出现了特定的注释
【解决方案3】:

它应该按类型自动装配,但是...

你命名了你的 UserDetailService

@Service("userService")

尝试将您的字段命名为相同

@Autowired
private UserDetailsService userService;

【讨论】:

  • 经过试验和测试,无法使用。之前也有人推荐过,然后他们编辑了他们的评论。让我觉得 Spring Security 的核心 UserDetailsS​​ervice 可能存在问题
  • 然后将@PostConstruct 方法添加到您的bean,检查它是否已创建。您还应该在启动应用程序时获取 spring 日志 - 它显示已创建的 bean。您还可以自动装配 ApplicationContext ctx;看看 ctx.getBeanDefinitionNames()
  • 谢谢,检查了一下,无法编译它,因为它抛出了那个错误,但是我的 IDE 选择了 bean。
【解决方案4】:

如果尝试将此注解添加到 SecurityConfig 类会怎样?

@ComponentScan(basePackages = "包含 UserDetailsS​​ervice 的包")

【讨论】:

  • 扫描工作正常。如前所述,我在同一目录中获得了 6 项其他服务,并且一切正常。该错误似乎来自 Spring Security UserDetails,而不是我个人的代码实现。
  • 添加这个以防万一,我已经阅读了本教程:packtpub.com/article/… 在示例中,authenticationManager 具有以下内容:[code] 身份验证管理器>
  • 我认为这是正确的解决方案。扫描是在子上下文中完成的,应该在父上下文中完成。从 mvc 配置中删除扫描并添加此注释。
猜你喜欢
  • 2014-08-22
  • 2023-03-13
  • 2016-03-23
  • 1970-01-01
  • 2016-05-03
  • 2016-03-29
  • 1970-01-01
  • 1970-01-01
  • 2016-02-23
相关资源
最近更新 更多