web-dev-qa-db-fra.com

Comment configurer Spring-Security pour accéder aux détails de l'utilisateur dans la base de données?

Je suis perplexe avec SpringSecurity. Il existe de nombreuses façons d'implémenter une chose simple et je les ai toutes mélangées.

Mon code est le suivant mais il lève une exception. Si je supprime UserDetailsService les codes associés, l'application s'exécute et je peux me connecter in-memory utilisateurs. Comme suggéré ci-dessous, j'ai converti la configuration en XML, mais les utilisateurs ne peuvent pas se connecter.

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:  
org.springframework.security.core.userdetails.UserDetailsService 
com.myproj.config.SecurityConfig.userDetailsService; 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.annotation.Qualifier(value=userDetailsService)}

Caused by: org.springframework.beans.factory.BeanCreationException: Could not 
autowire field 

org.springframework.security.core.userdetails.UserDetailsService 
com.myproj.config.SecurityConfig.userDetailsService; 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.annotation.Qualifier(value=userDetailsService)}

Caused by: 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.annotation.Qualifier(value=userDetailsService)}

Web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://Java.Sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://Java.Sun.com/xml/ns/javaee 
          http://Java.Sun.com/xml/ns/javaee/web-app_3_0.xsd"
    version="3.0">
    <listener>
        <listener-class>org.Apache.tiles.extras.complete.CompleteAutoloadTilesListener</listener-class>
    </listener>

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

    <servlet-mapping>
      <servlet-name>proj</servlet-name>
      <url-pattern>/</url-pattern>
    </servlet-mapping>



</web-app>

MvcWebApplicationInitializer

import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;


public class MvcWebApplicationInitializer
    extends AbstractAnnotationConfigDispatcherServletInitializer {

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

    @Override
    protected Class<?>[] getServletConfigClasses() {
        return null;
    }

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

}

SecurityWebApplicationInitializer

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

public class SecurityWebApplicationInitializer
extends AbstractSecurityWebApplicationInitializer {

}

SecurityConfig

@Configuration
@EnableWebMvcSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    @Qualifier("userDetailsService")
    UserDetailsService userDetailsService;

    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth)
            throws Exception {
        auth.userDetailsService(userDetailsService).passwordEncoder(
                passwordEncoder());
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .antMatchers("/resources/**", "/", "/index", "/aboutus")
                .permitAll()
                .antMatchers("/profile/**")
                .hasRole("USER")
                .and()
                .formLogin().loginPage("/signin").failureUrl("/signin?error")
                .permitAll().and().logout().logoutUrl("/signout").permitAll();

    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        PasswordEncoder encoder = new BCryptPasswordEncoder();
        return encoder;
    }

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

}

MemberServiceImpl

@Service("userDetailsService")
public class MemberServiceImpl implements UserDetailsService {

    @Autowired
    MemberRepository memberRepository;

    private List<GrantedAuthority> buildUserAuthority(String role) {
        Set<GrantedAuthority> setAuths = new HashSet<GrantedAuthority>();
        setAuths.add(new SimpleGrantedAuthority(role));
        List<GrantedAuthority> result = new ArrayList<GrantedAuthority>(
                setAuths);
        return result;
    }

    private User buildUserForAuthentication(Member member,
            List<GrantedAuthority> authorities) {
        return new User(member.getEmail(), member.getPassword(),
                member.isEnabled(), true, true, true, authorities);
    }

    @Override
    @Transactional(readOnly = true)
    public UserDetails loadUserByUsername(String username)
            throws UsernameNotFoundException {
        Member member = memberRepository.findByUserName(username);
        List<GrantedAuthority> authorities = buildUserAuthority("Role");
        return buildUserForAuthentication(member, authorities);
    }

}

mise à jour 1

Même après avoir ajouté l'annotation suivante et la méthode authenticationManagerBean de SecurityConfig, la même exception est levée.

    @EnableGlobalMethodSecurity(prePostEnabled = true)

mise à jour 2

Comme suggéré dans l'une des réponses, je l'ai converti en configuration basée sur XML, le code actuel est le suivant; cependant, lorsque je soumets un formulaire de connexion, il ne fait rien.

Spring-Security.xml

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



    <beans:import resource='login-service.xml' />
    <http auto-config="true" access-denied-page="/notFound.jsp"
        use-expressions="true">
        <intercept-url pattern="/" access="permitAll" />


        <form-login login-page="/signin" authentication-failure-url="/signin?error=1"
            default-target-url="/index" />
        <remember-me />
        <logout logout-success-url="/index.jsp" />
    </http>
    <authentication-manager>
        <authentication-provider>
            <!-- <user-service> <user name="admin" password="secret" authorities="ROLE_ADMIN"/> 
                <user name="user" password="secret" authorities="ROLE_USER"/> </user-service> -->
            <jdbc-user-service data-source-ref="dataSource"

                users-by-username-query="
              select username,password,enabled 
              from Member where username=?"

                authorities-by-username-query="
                      select username 
                      from Member where username = ?" />
        </authentication-provider>
    </authentication-manager>
</beans:beans>

login-service.xml

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

   <bean id="dataSource"
    class="org.springframework.jdbc.datasource.DriverManagerDataSource">

    <property name="driverClassName" value="com.mysql.jdbc.Driver" />
    <property name="url" value="jdbc:mysql://localhost/testProject" />
    <property name="username" value="root" />
    <property name="password" value="" />
   </bean>

</beans>
29
Daniel Newtown

Je pense que vous oubliez d'ajouter cette annotation sur la classe SecurityConfig

@Configuration
@EnableWebMvcSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    @Qualifier("userDetailsService")
    UserDetailsService userDetailsService;

    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth)
            throws Exception {
        auth.userDetailsService(userDetailsService).passwordEncoder(
                passwordEncoder());
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .antMatchers("/resources/**", "/", "/index", "/aboutus")
                .permitAll().antMatchers("/profile/**").hasRole("USER").and()
                .formLogin().loginPage("/signin").failureUrl("/signin?error")
                .permitAll().and().logout().logoutUrl("/signout").permitAll();

    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        PasswordEncoder encoder = new BCryptPasswordEncoder();
        return encoder;
    }

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

et une chose de plus je pense que ce haricot n'est pas nécessaire

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

Veuillez essayer cet espoir que cela fonctionnera pour vous ..

Pour obtenir l'utilisateur actuel

public String getUsername() {
        SecurityContext context = SecurityContextHolder.getContext();
        Authentication authentication = context.getAuthentication();
        if (authentication == null)
            return null;
        Object principal = authentication.getPrincipal();
        if (principal instanceof UserDetails) {
            return ((UserDetails) principal).getUsername();
        } else {
            return principal.toString();
        }
    }


    public User getCurrentUser() {
        if (overridenCurrentUser != null) {
            return overridenCurrentUser;
        }
        User user = userRepository.findByUsername(getUsername());

        if (user == null)
            return user;
    }

Merci

19
Charnjeet Singh

Je pense que le problème pourrait être dû à l'absence de @ComponentScan annotation. Lors d'une tentative de câblage automatique userDetailsService dans SecurityConfig, il n'est pas en mesure de trouver un bean approprié pour le câblage automatique.

Une application Spring a généralement un "contexte d'application" distinct, en plus du "contexte mvc", du "contexte de sécurité" (que vous avez déjà via SecurityConfig), etc.

Je ne sais pas si mettre @ComponentScan on SecurityConfig lui-même ne fonctionnera pas, mais vous pouvez l'essayer:

@Configuration
@ComponentScan("your_base_package_name_here")
@EnableWebMvcSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
...
}

Remplacez "your_base_package_name_here" par le nom du package contenant votre @Component ou @Service Des classes.

Si cela ne fonctionne pas, ajoutez une nouvelle classe vide avec @ComponentScan annotation:

@Configuration
@ComponentScan("your_base_package_name_here")
public class AppConfig {
    // Blank
}

Source: http://docs.spring.io/spring-javaconfig/docs/1.0.0.M4/reference/html/ch06s02.html

15
The Student Soul

Voir qu'il y a des erreurs dans votre base de code, essayez de le résoudre en voyant le code ci-dessous.

Supprimez votre fichier SecurityConfig et convertissez-le en une configuration basée sur un fichier xml.

Votre spring-security.xml devrait ressembler à ceci.

   <security:http auto-config="true" >  
  <security:intercept-url pattern="/index*" access="ROLE_USER" />  
  <security:form-login login-page="/login" default-target-url="/index"  
   authentication-failure-url="/fail2login" />  
  <security:logout logout-success-url="/logout" />  
 </security:http>  

    <security:authentication-manager>  
   <security:authentication-provider>  
     <!-- <security:user-service>  
   <security:user name="samplename" password="sweety" authorities="ROLE_USER" />  
     </security:user-service> -->  
     <security:jdbc-user-service data-source-ref="dataSource"    
      users-by-username-query="select username, password, active from users where username=?"   
          authorities-by-username-query="select us.username, ur.authority from users us, user_roles ur   
        where us.user_id = ur.user_id and us.username =?  "   
  />  
   </security:authentication-provider>  
 </security:authentication-manager>  

web.xml devrait ressembler à ceci:

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

 <servlet-mapping>  
  <servlet-name>sdnext</servlet-name>  
  <url-pattern>/</url-pattern>  
 </servlet-mapping>  
 <listener>  
  <listener-class>  
                  org.springframework.web.context.ContextLoaderListener  
        </listener-class>  
 </listener>  

 <context-param>  
  <param-name>contextConfigLocation</param-name>  
  <param-value>  
   /WEB-INF/sdnext-*.xml,  
  </param-value>  
 </context-param>  

 <welcome-file-list>  
  <welcome-file>index</welcome-file>  
 </welcome-file-list>  

 <!-- Spring Security -->  
 <filter>  
  <filter-name>springSecurityFilterChain</filter-name>  
  <filter-class>  
                  org.springframework.web.filter.DelegatingFilterProxy  
                </filter-class>  
 </filter>  

 <filter-mapping>  
  <filter-name>springSecurityFilterChain</filter-name>  
  <url-pattern>/*</url-pattern>  
 </filter-mapping>  
6
MS Ibrahim

Essayez d'ajouter la méthode suivante à votre SecurityConfig:

@Bean
public UserDetailsService userDetailsServiceBean() throws Exception {
    return super.userDetailsServiceBean();
}
3
fjmodi

Spring ne peut pas trouver le bean avec le qualificatif userDetailsService. Je pense que vous devriez vérifier votre fichier applicationContext.xml Au cas où vous auriez oublié de configurer le bean de UserDetailsService pour Spring Security. S'il y est, essayez une fois en supprimant @Qualifier("userDetailsService").

suivez ce lien. fichier context.xml configuré par rapport à la sécurité Spring

2
Pramod Gaikwad

Il semble que votre bean "userDetailsService" est déclaré @ Autowired, mais il n'est pas disponible en tant que classe ( MemberServiceImpl ) dans le contexte de votre SecurityConfig .

Je suppose que dans votre MvcWebApplicationInitializer , vous devez inclure MemberServiceImpl comme:

@Override
protected Class<?>[] getRootConfigClasses() {
    return new Class[] { SecurityConfig.class, MemberServiceImpl.class };
}
2
sanastasiadis

Voici la réponse, en utilisant le @ComponentScan approprié, vous trouverez ci-dessous un exemple d'extrait de code que je colle, que j'ai également rencontré le même problème et résolu. Ci-dessous est résolu et fonctionne pour le problème lié à l'exception de création de bean pour org.springframework.security.core.userdetails.UserDetailsService


Step1: Écrivez la classe de configuration d'application de sécurité

import org.springframework.security.core.userdetails.UserDetailsService;

    @Configuration
    @EnableWebSecurity
    public class LoginSecurityConfig extends WebSecurityConfigurerAdapter {

        @Autowired
        @Qualifier("userDetailsServiceImpl")
        UserDetailsService userDetailsService;

Ici, @ComponentScan n'est pas obligatoire dans LoginSecurityConfig , vous pouvez définir @ComponentScan dans la classe de configuration racine comme ci-dessous et importer le LoginSecurityConfig.class LoginSecurityConfig.

@Configuration
@EnableWebMvc
@ComponentScan(basePackages = { "com.example" })
@Import(value = { LoginSecurityConfig.class })
public class LoginApplicationConfig 

Step2: Maintenant, câblage automatique du SpringBean org.springframework.security.core.userdetails.UserDetailsService

@Service("userDetailsServiceImpl")
public class UserDetailsServiceImpl implements org.springframework.security.core.userdetails.UserDetailsService {

@Autowired
UserDao userDao;

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

User user = userDao.findByUsername(username);

if (user == null) {
            System.out.println("User not found");
            throw new UsernameNotFoundException("Username not found");
}


  return new org.springframework.security.core.userdetails.User(user.getUsername(), user.getPassword(), true, true, true, true, getGrantedAuthorities(user));
}

private List<GrantedAuthority> getGrantedAuthorities(User user) {
    List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();

    authorities.add(new SimpleGrantedAuthority("ROLE_USER"));
    return authorities;
}

    }//End of Class
1
Pavan

Essayez de changer le type de champ:

@Configuration
@EnableWebMvcSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    @Qualifier("userDetailsService")
    MemberServiceImpl userDetailsService;
1

J'utilise Wicket et j'ai rencontré le même problème. Je pourrais résoudre ce problème en modifiant l'ordre dans ma classe AppInit pour analyser le package en premier, puis enregistrer le bean appelant

public class AppInit implements WebApplicationInitializer {
    @Override
    public void onStartup(ServletContext servletContext) throws ServletException 
{

        // Create webapp context
        AnnotationConfigWebApplicationContext root = new AnnotationConfigWebApplicationContext();
        root.scan("my_package");
        root.register(SpringSecurityConfiguration.class);
...#
}
0
Phash