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

Aes*_*eir 9 java spring spring-security autowired

我是Spring的新手,所以我一直在寻找安全方面的东西.每次我运行我的应用程序时,我得到:

org.springframework.beans.factory.BeanCreationException:创建名为'securityConfig'的bean时出错:注入自动连接的依赖项失败; 嵌套异常是org.springframework.beans.factory.BeanCreationException:无法自动装配字段:private 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:期望至少有一个bean符合此依赖项的autowire候选资格.依赖注释:{@ org.springframework.beans.factory.annotation.Autowired(required = true)}

我用精梳梳理了我的代码,无法查明问题.

Spring Framework版本: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();
    }
}
Run Code Online (Sandbox Code Playgroud)

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

没有合理的理由说明为什么它应该失败呢.

注意:

我的IDE可以链接autowire(即它知道自动装配的位置和所有内容),但编译失败.

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

@Autowired
private UserDataService userDataService;
Run Code Online (Sandbox Code Playgroud)

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

所以这个问题只与SecurityConfig.java这样的配置文件隔离开来,这是唯一一次无效的问题.

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

SecurityInitializer.java

@Order(2)
public class SecurityInitializer extends AbstractSecurityWebApplicationInitializer  {
}
Run Code Online (Sandbox Code Playgroud)

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};
//    }
}
Run Code Online (Sandbox Code Playgroud)

MVC-调度-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>
Run Code Online (Sandbox Code Playgroud)

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>
Run Code Online (Sandbox Code Playgroud)

Rob*_*nch 15

更新(提供完整示例后)

看来你有很多问题.

web.xml与AbstractAnnotationConfigDispatcherServletInitializer

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

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

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

getRootConfigClasses vs getServletConfigClasses

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

getServletConfigClasses中的任何配置通常称为子配置,并且可以查看getRootConfigClasses中定义的bean.getServletConfigClasses配置DispatcherServlet并且必须包含Spring MVC的bean(即Controllers,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中作为<context:component-scan base-package ="com.entirety.app"/>进行

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

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

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

public class SecurityWebApplicationInitializer
      extends AbstractSecurityWebApplicationInitializer {
}
Run Code Online (Sandbox Code Playgroud)

您是如何导入安全配置的?如果Spring Security配置位于根上下文中,则它将不会看到Dispatcher Servlet上下文中定义的bean.

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

public class SecurityWebApplicationInitializer
      extends AbstractSecurityWebApplicationInitializer {
    protected String getDispatcherWebApplicationContextSuffix() {
        return "mvc";
    }
}
Run Code Online (Sandbox Code Playgroud)