Java 无法自动装配字段:私有 org.springframework.security.core.userdetails.UserDetailsS​​ervice

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/21526132/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-13 09:10:40  来源:igfitidea点击:

Could not autowire field: private org.springframework.security.core.userdetails.UserDetailsService

javaspringspring-securityautowired

提问by Aeseir

I'm new to Spring so I been mucking around with security side of things. Everytime I run my application I get:

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

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: private org.springframework.security.core.userdetails.UserDetailsService com.entirety.app.config.SecurityConfig.userDetailsServiceImplementation; 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.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:未找到类型为 [org.springframework.security.core.userdetails.UserDetailsS​​ervice] 的合格 bean 依赖项:预期至少有 1 个 bean 有资格作为此依赖项的自动装配候选者。依赖注解:{@org.springframework.beans.factory.annotation.Autowired(required=true)}

I've gone through my code with a fine comb and can't pinpoint the issue.

我已经仔细检查了我的代码,但无法查明问题所在。

Spring Framework Version: 3.2.5.RELEASE Spring Security Version: 3.2.0.M2

Spring 框架版本:3.2.5.RELEASE Spring 安全版本: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

客户服务.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;
    }
}

There is no logical reason why it should be failing yet it does.

没有合乎逻辑的理由为什么它应该失败,但它确实失败了。

NOTE:

笔记:

My IDE can link the autowire (that is it knows where its being autowired from and everything) yet compiling it fails.

我的 IDE 可以链接自动装配(即它知道从哪里自动装配以及所有内容)但编译失败。

EDIT 2: I've removed the following code from SecurityConfig.java file

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

@Autowired
private UserDataService userDataService;

And added it to one of the POJOs and Controllers I know are called on regular basis and contain other services. In this case pasted that code into my baseController.java file which directs to rendering the homepage. The service is compile properly and i can even call it within the controller.

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

So the problem is isolated only to the config file such as SecurityConfig.java, thats the only time it doesn't work.

因此,问题仅与配置文件(例如 SecurityConfig.java)隔离,这是唯一一次不起作用。

EDIT 3: Added extra files

编辑 3:添加了额外的文件

SecurityInitializer.java

安全初始化器.java

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

WebInitializer.java

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

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

网页.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>

采纳答案by Rob Winch

Update (after providing complete example)

更新(提供完整示例后)

It seems you have multiple issues.

看来你有多个问题。

web.xml vs AbstractAnnotationConfigDispatcherServletInitializer

web.xml 与 AbstractAnnotationConfigDispatcherServletInitializer

The application you created had a web.xml that configures a DispatcherServlet named mvc-dispatcher. The mvc-dispatcherhad a configuration of mvc-dispatcher-servlet.xmlwhich loaded all beans within the package com.springapp.sectest. This means that mvc-dispatchercan find your UserDetailsService.

您创建的应用程序有一个 web.xml,它配置了一个名为 的 DispatcherServlet mvc-dispatcher。所述mvc-dispatcher具有的结构mvc-dispatcher-servlet.xml,其装载在封装之内的所有豆com.springapp.sectest。这意味着mvc-dispatcher可以找到您的UserDetailsService.

The application also had a AbstractAnnotationConfigDispatcherServletInitializer which created a DispatcherServletthat loaded your WebConfigjava configuration. This DispatcherServletcould not see any Beans created by mvc-dispatcher.

该应用程序还有一个 AbstractAnnotationConfigDispatcherServletInitializer,它创建了一个DispatcherServlet加载你的WebConfigjava 配置的。这DispatcherServlet看不到任何由mvc-dispatcher.

In short, you should either configure a DispatcherServletusing web.xml or AbstractAnnotationConfigDispatcherServletInitializerand NOT both.

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

getRootConfigClasses vs getServletConfigClasses

getRootConfigClasses 与 getServletConfigClasses

Any configuration within getRootConfigClasses is typically called the root or parent configuration and cannot view beans defined in getServletConfigClasses. Spring Security protects your application defined in the getRootConfigClasses with a Filter named springSecurityFilterChain that is created by the @EnableWebSecurityannotation. This means it is generally best to place Spring Security's configuration in the getRootConfigClasses. There are some exceptions to this, like if you want to do method security on your Spring MVC Controllers.

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

Any configuration within getServletConfigClasses is typically called the child configuration and can view beans defined in the getRootConfigClasses. The getServletConfigClasses configures the DispatcherServletand must contain beans for Spring MVC (i.e. Controllers, ViewResovlers, etc). Any Spring MVC beans defined in getRootConfigClasses are visible but not used.

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

This setup, while a bit confusing, allows you to have multiple DispatcherServletinstances with isolated configurations. In practice, it is VERY rare to have multiple DispatcherServletinstances though.

这种设置虽然有点混乱,但允许您拥有多个DispatcherServlet具有独立配置的实例。但实际上,拥有多个DispatcherServlet实例的情况非常罕见。

Given the information above, you should move the UserDetailsServicedeclaration along with all of its dependent beans to the getRootConfigClasses. This will ensure the SecurityConfig.java can find the UserDetailsService.

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

Pull Request for fixes

请求修复

I have submitted a PR to get your application so that it starts with no errors https://github.com/worldcombined/AnnotateFailed/pull/1. A few notes

我已经提交了一个 PR 来获取您的应用程序,以便它开始时没有错误https://github.com/worldcombined/AnnotateFailed/pull/1。一些注意事项

  • the test does not use a parent and child context so it is not reflecting running the application
  • I had to move the resources folder so that was picked up properly
  • I did not make it so you can actually authenticate. There was no login.jsp and the UserDetailsService you provided always returns null Instead I attempted to demonstrate that the error on here was answered.
  • 测试不使用父子上下文,因此它不反映运行应用程序
  • 我不得不移动资源文件夹,以便正确拾取
  • 我没有这样做,因此您可以实际进行身份验证。没有 login.jsp 并且您提供的 UserDetailsS​​ervice 总是返回 null 而我试图证明这里的错误得到了回答。

Original Answer

原答案

Based upon this comment:

基于此评论:

@ComponentScan is undertaken in my mvc-dispatcher-servlet.xml as <context:component-scan base-package="com.entirety.app"/>

@ComponentScan 在我的 mvc-dispatcher-servlet.xml 中作为 <context:component-scan base-package="com.entirety.app"/> 进行

It appears that you are configuring the services within the Dispatcher configuration and not within the root context.

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

Spring Security's configuration is typically configured in the root context. For example, one might extend the AbstractSecurityWebApplicationInitializer as shown below:

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

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

public class SecurityWebApplicationInitializer
      extends AbstractSecurityWebApplicationInitializer {
}

How are you importing the Security configuration? If the Spring Security configuration is in the root context then it will not see beans defined in the Dispatcher Servlet context.

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

The solution is to move the configuring of your services in the root context or move the Spring Security configuration to the dispatcher's ApplicationContext. For example, moving Spring Security configuration to the dispatcher context would look like the following if your dispatcher servlet was named mvc.

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

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

回答by Abhinav Jayaram

use @ component for SecurityConfig class

将 @ 组件用于 SecurityConfig 类

回答by freakman

it should be autowired by type but...

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

you named your UserDetailService

你命名了你的 UserDetailService

@Service("userService")

try to name your field the same

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

@Autowired
private UserDetailsService userService;

回答by indybee

What if try adding this annotation to SecurityConfig class?

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

@ComponentScan( basePackages = "package containing UserDetailsService" )

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