web-dev-qa-db-fra.com

Champ autowire impossible: org.springframework.security.core.userdetails.UserDetailsService

Je suis nouveau au printemps alors je bricole avec des problèmes de sécurité ..__ À chaque fois que je lance mon application, je reçois:

org.springframework.beans.factory.BeanCreationException: Erreur lors de la création d'un bean avec le nom 'securityConfig': L'injection de dépendances autowired a échoué; L'exception imbriquée est org.springframework.beans.factory.BeanCreationException: le champ autowire ne peut pas être: privé org.springframework.security.core.userdetails.UserDetailsService com.entirety.app.config.SecurityConfig.userDetailsServiceImplémentation L'exception imbriquée est org.springframework.beans.factory.NoSuchBeanDefinitionException: aucun bean de qualification de type [org.springframework.security.core.userdetails.UserDetailsService] n'a été trouvé pour la dépendance: attendu au moins 1 bean qui se qualifie comme candidat à l'autonomie pour cette dépendance. Annotations de dépendance: {@ org.springframework.beans.factory.annotation.Autowired (required = true)}

J'ai parcouru mon code avec un peigne fin et je ne peux pas identifier le problème.

Version de la structure Spring: 3.2.5.RELEASE Version de la sécurité 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

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

Il n’existe aucune raison logique pour laquelle il devrait échouer, mais c’est le cas.

REMARQUE:

Mon IDE peut lier le fil automatique (c'est-à-dire qu'il sait d'où il provient et tout le reste), mais sa compilation échoue.

EDIT 2: J'ai supprimé le code suivant du fichier SecurityConfig.Java

@Autowired
private UserDataService userDataService;

Et l'ajouté à l'un des POJO et des contrôleurs que je connais sont appelés régulièrement et contiennent d'autres services . Dans ce cas-ci, ce code a été collé dans mon fichier baseController.Java qui indique le rendu de la page d'accueil . Le service est compiler correctement et je peux même l'appeler dans le contrôleur.

Le problème n’est donc isolé que dans le fichier de configuration tel que SecurityConfig.Java, c’est la seule fois où il ne fonctionne pas.

EDIT 3: Ajout de fichiers supplémentaires

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>
9
Aeseir

Mise à jour (après avoir fourni un exemple complet)

Il semble que vous ayez de multiples problèmes.

web.xml vs AbstractAnnotationConfigDispatcherServletInitializer

L’application que vous avez créée avait un fichier web.xml qui configurait un DispatcherServlet nommé mvc-dispatcher. Le mvc-dispatcher avait une configuration de mvc-dispatcher-servlet.xml qui chargeait tous les beans du paquetage com.springapp.sectest. Cela signifie que mvc-dispatcher peut trouver votre UserDetailsService.

L'application disposait également d'un AbstractAnnotationConfigDispatcherServletInitializer qui a créé une DispatcherServlet qui a chargé votre configuration WebConfig Java. Cette DispatcherServlet n'a pu voir aucun Bean créé par mvc-dispatcher.

En bref, vous devez soit configurer une DispatcherServlet en utilisant web.xml ou AbstractAnnotationConfigDispatcherServletInitializer et PAS les deux.

getRootConfigClasses vs getServletConfigClasses

Toute configuration dans getRootConfigClasses est généralement appelée configuration racine ou parent et ne peut pas afficher les beans définis dans getServletConfigClasses. Spring Security protège votre application définie dans les getRootConfigClasses avec un filtre nommé springSecurityFilterChain créé par l'annotation @EnableWebSecurity. Cela signifie qu'il est généralement préférable de placer la configuration de Spring Security dans les getRootConfigClasses. Il existe quelques exceptions à cela, comme si vous souhaitez sécuriser les méthodes sur vos contrôleurs Spring MVC.

Toute configuration dans getServletConfigClasses est généralement appelée configuration enfant et peut afficher les beans définis dans getRootConfigClasses. La méthode getServletConfigClasses configure la DispatcherServlet et doit contenir des beans pour Spring MVC (c'est-à-dire des contrôleurs, des ViewResovlers, etc.). Tous les beans Spring MVC définis dans getRootConfigClasses sont visibles mais non utilisés.

Cette configuration, même si elle est un peu déroutante, vous permet d’avoir plusieurs instances DispatcherServlet avec des configurations isolées. En pratique, il est TRÈS rare d'avoir plusieurs instances DispatcherServlet.

Compte tenu des informations ci-dessus, vous devez déplacer la déclaration UserDetailsService avec tous ses beans dépendants dans les classes getRootConfig. Cela permettra à SecurityConfig.Java de trouver la UserDetailsService.

Pull Request pour des correctifs

J'ai soumis un PR pour que votre candidature soit enregistrée sans erreur de départ https://github.com/worldcombined/AnnotateFailed/pull/1 . Quelques notes

  • le test n'utilise pas les contextes parent et enfant, il ne reflète donc pas l'exécution de l'application
  • Je devais déplacer le dossier des ressources pour que cela soit pris correctement
  • Je ne l'ai pas fait pour que vous puissiez vous authentifier. Il n'y avait pas de login.jsp et le service UserDetailsService que vous avez fourni renvoie toujours null. Au lieu de cela, j'ai tenté de démontrer que l'erreur avait été résolue.

Réponse originale

Basé sur ce commentaire:

@ComponentScan est exécuté dans mon fichier mvc-dispatcher-servlet.xml en tant que <contexte: composant-scan base-package = "com.entirety.app" />

Il semble que vous configuriez les services dans la configuration de Dispatcher et non dans le contexte racine.

La configuration de Spring Security est généralement configurée dans le contexte racine. Par exemple, on pourrait étendre AbstractSecurityWebApplicationInitializer comme indiqué ci-dessous:

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

public class SecurityWebApplicationInitializer
      extends AbstractSecurityWebApplicationInitializer {
}

Comment importez-vous la configuration de la sécurité? Si la configuration de Spring Security se trouve dans le contexte racine, les beans définis dans le contexte du servlet Dispatcher ne seront pas visibles.

La solution consiste à déplacer la configuration de vos services dans le contexte racine ou à déplacer la configuration de Spring Security vers ApplicationContext du répartiteur. Par exemple, le déplacement de la configuration de Spring Security dans le contexte du distributeur correspond à ce qui suit si votre servlet de distributeur s'appelle mvc.

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

utiliser @ composant pour la classe SecurityConfig

0
Abhinav Jayaram

Et si vous essayez d'ajouter cette annotation à la classe SecurityConfig?

@ComponentScan (basePackages = "paquet contenant UserDetailsService")

0
indybee

il devrait être auto-câblé par type mais ...

vous avez nommé votre UserDetailService

@Service("userService")

essayez de nommer votre champ de la même manière

@Autowired
private UserDetailsService userService;
0
freakman