4

Je suis un débutant de printemps et face à des problèmes avec Spring Security. J'essaye d'implémenter un UserDetailsService personnalisé pour l'extraction d'utilisateur et obtenant une exception de pointeur nul quand j'accède à l'objet UserService. Je suis autowiring cet objet. Le autowirng fonctionne bien quand il est fait sur d'autres méthodes de contrôleur et de service mais pour une raison quelconque, il ne fonctionne pas ici et donc j'obtiens l'exception de pointeur nul quand l'objet autowired (UserService) est accédé.Spring Security 3.1 + JPA - Exception pointeur nul

J'apprécierais vraiment l'aide à ce sujet.

Exception Trace de la pile:

java.lang.NullPointerException 
java.lang.NullPointerException 
at com.contact.list.service.CustomUserDetailsService.loadUserByUsername(CustomUserDetailsService.java:37) 
at org.springframework.security.authentication.dao.DaoAuthenticationProvider.retrieveUser(DaoAuthenticationProvider.java:81) 
at org.springframework.security.authentication.dao.AbstractUserDetailsAuthenticationProvider.authenticate(AbstractUserDetailsAuthenticationProvider.java:132) 
at org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:156) 
at org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:174) 
at org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter.attemptAuthentication(UsernamePasswordAuthenticationFilter.java:94) 
at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:194) 
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:323) 
at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:105) 
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:323) 
at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:87) 
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:323) 
at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:173) 
at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:346) 
at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:259) 
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:243) 
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210) 
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:225) 
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:123) 
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:472) 
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:168) 
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:98) 
at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:927) 
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:118) 
at com.springsource.insight.collection.tcserver.request.HttpRequestOperationCollectionValve.invoke(HttpRequestOperationCollectionValve.java:88) 
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:407) 
at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1001) 
at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:585) 
at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:310) 
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1110) 
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:603) 
at java.lang.Thread.run(Thread.java:722) 

CustomUserDetailsService Classe:

package com.contact.list.service; 

import java.util.ArrayList; 
import java.util.Collection; 
import java.util.List; 

import org.springframework.beans.factory.annotation.Autowired; 
import org.springframework.beans.factory.annotation.Configurable; 
import org.springframework.context.ApplicationContext; 
import org.springframework.context.support.ClassPathXmlApplicationContext; 
import org.springframework.context.support.GenericXmlApplicationContext; 
import org.springframework.security.core.GrantedAuthority; 
import org.springframework.security.core.authority.SimpleGrantedAuthority; 
import org.springframework.security.core.userdetails.User; 
import org.springframework.security.core.userdetails.UserDetails; 
import org.springframework.security.core.userdetails.UserDetailsService; 
import org.springframework.security.core.userdetails.UsernameNotFoundException; 
import org.springframework.stereotype.Repository; 
import org.springframework.stereotype.Service; 
import org.springframework.transaction.annotation.Transactional; 

import com.contact.list.form.Role; 
import com.contact.list.repository.UserRepository; 

@Service 
@Transactional(readOnly = true) 
public class CustomUserDetailsService implements UserDetailsService { 

@Autowired 
private UserService userService; 


public UserDetails loadUserByUsername(String username) 
     throws UsernameNotFoundException { 

    try{ 
     com.contact.list.form.User domainuser =  userService.findByUsername(username); 


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

     return new User(domainuser.getUsername(), 
         domainuser.getPassword().toLowerCase(), 
         enabled,accountNonExpired, 
         credentialsNonExpired, 
         accountNonLocked, 
         getAuthorities(domainuser.getRoles()) 
       ); 


    }catch (Exception e){ 
     System.out.println(e); 
     e.printStackTrace(); 
     throw new RuntimeException(e); 
    } 


} 

public Collection<? extends GrantedAuthority> getAuthorities(List<Role> roles){ 

    List<GrantedAuthority> authList = getGrantedAuthorities(getroles(roles)); 
    return authList; 
} 

public static List<GrantedAuthority> getGrantedAuthorities(List<String> userroles){ 

    List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>(); 
    for(String userrole:userroles){ 
     authorities.add(new SimpleGrantedAuthority(userrole)); 
    } 
    return authorities; 
} 

public List<String> getroles(List<Role> roles){ 

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

    for (Role role : roles){ 

    if(role.getRole() == 1){ 
     userroles.add("ROLE_USER"); 
    } 
    if(role.getRole() == 2){ 
     userroles.add("ROLE_ADMIN"); 
    } 

    } 

    return userroles; 
} 




} 

UserService Interface:

package com.contact.list.service; 

import java.util.List; 

import com.contact.list.form.Contact; 
import com.contact.list.form.User; 

public interface UserService { 

public List<User> findAll(); 

public void save(User user); 

public User findByUsername(String username); 
} 

UserService classe de mise en œuvre:

package com.contact.list.service; 

    import java.util.List; 

    import org.springframework.beans.factory.annotation.Autowired; 
    import org.springframework.stereotype.Repository; 
    import org.springframework.stereotype.Service; 
    import org.springframework.transaction.annotation.Transactional; 

    import com.contact.list.form.Contact; 
    import com.contact.list.form.User; 
    import com.contact.list.repository.UserRepository; 
    import com.google.common.collect.Lists; 


    @Service 
    @Repository 
    @Transactional 
    public class UserServiceImpl implements UserService { 

@Autowired 
private UserRepository userrepository; 

public void save(User user) { 

    userrepository.save(user); 
} 

@Transactional(readOnly=true) 
public List<User> findAll() { 
    return Lists.newArrayList(userrepository.findAll()); 
} 

public User findByUsername(String username){ 

    return userrepository.findByUsername(username); 

} 

    } 

Userrepository:

package com.contact.list.repository; 

import org.springframework.data.repository.CrudRepository; 

import com.contact.list.form.User; 

public interface UserRepository extends CrudRepository<User, Long> { 

User findByUsername(String username); 

} 

Classe utilisateur:

package com.contact.list.form; 

import java.util.ArrayList; 
import java.util.List; 

import javax.persistence.CascadeType; 
import javax.persistence.Column; 
import javax.persistence.Entity; 
import javax.persistence.GeneratedValue; 
import javax.persistence.Id; 
import javax.persistence.OneToMany; 
import javax.persistence.Table; 


@Entity 
@Table(name = "USER_TBL") 
public class User { 



@Column(name = "FIRST_NAME") 
private String firstName; 

@Column(name = "LAST_NAME") 
private String lastName; 

@Column(name = "EMAIL") 
private String email; 

@Id 
@Column(name = "USERID") 
private String username; 

@Column(name = "PASSWORD") 
private String password; 

@OneToMany(mappedBy = "user", cascade = CascadeType.ALL) 
private List<Role> roles = new ArrayList<Role>(); 


public String getFirstName() { 
    return firstName; 
} 
public void setFirstName(String firstName) { 
    this.firstName = firstName; 
} 
public String getLastName() { 
    return lastName; 
} 
public void setLastName(String lastName) { 
    this.lastName = lastName; 
} 
public String getUsername() { 
    return username; 
} 
public void setUsername(String username) { 
    this.username = username; 
} 
public String getPassword() { 
    return password; 
} 
public void setPassword(String password) { 
    this.password = password; 
} 
public String getEmail() { 
    return email; 
} 
public void setEmail(String email) { 
    this.email = email; 
} 
public List<Role> getRoles() { 
    return roles; 
} 
public void setRoles(List<Role> roles) { 
    this.roles = roles; 
} 


} 

web.xml:

<!-- Spring Security Configuration --> 
<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> 

<!-- The definition of the Root Spring Container shared by all Servlets and Filters --> 
<context-param> 
    <param-name>contextConfigLocation</param-name> 
    <param-value> 
    /WEB-INF/spring/root-context.xml 
    /WEB-INF/spring-security.xml 
    </param-value> 
</context-param> 


<!-- Creates the Spring Container shared by all Servlets and Filters --> 
<listener> 
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> 
</listener> 

<!-- Processes application requests --> 
<servlet> 
    <servlet-name>appServlet</servlet-name> 
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> 
     <init-param> 
     <param-name>contextConfigLocation</param-name> 
     <param-value> 
      /WEB-INF/spring/appServlet/servlet-context.xml 
     </param-value> 
     </init-param> 
    <load-on-startup>1</load-on-startup> 
</servlet> 

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

servlet-context.xml:

<!-- DispatcherServlet Context: defines this servlet's request-processing infrastructure --> 

<!-- Enables the Spring MVC @Controller programming model --> 
<annotation-driven /> 

<!-- Handles HTTP GET requests for /resources/** by efficiently serving up static resources in the ${webappRoot}/resources directory --> 
<resources mapping="/resources/**" location="/resources/" /> 

<interceptors> 
    <beans:bean class="org.springframework.web.servlet.theme.ThemeChangeInterceptor"/> 
</interceptors> 

<beans:bean id="themeSource" class="org.springframework.ui.context.support.ResourceBundleThemeSource"/> 

<beans:bean id="themeResolver" class="org.springframework.web.servlet.theme.CookieThemeResolver"/> 




<!-- Resolves views selected for rendering by @Controllers to .jsp resources in the /WEB-INF/views directory --> 
<beans:bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> 
    <beans:property name="prefix" value="/WEB-INF/views/" /> 
    <beans:property name="suffix" value=".jsp" /> 
    <beans:property name="requestContextAttribute" value="requestContext"/> 
</beans:bean> 

<context:component-scan base-package="com.contact.list" /> 

<beans:bean id = "myDataSource" class = "org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> 
    <beans:property name="driverClassName" value = "org.postgresql.Driver"/> 
    <beans:property name="url" value = "jdbc:postgresql://localhost:5432/hibernatedb"/> 
    <beans:property name="username" value = "postgres"/> 
    <beans:property name="password" value = "password"/> 
</beans:bean> 


<!-- JPA Config --> 

<beans:bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager"> 
    <beans:property name="entityManagerFactory" ref="emf"/> 
</beans:bean> 

<tx:annotation-driven transaction-manager="transactionManager" /> 

<beans:bean id = "emf" class = "org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"> 
    <beans:property name = "dataSource" ref = "myDataSource"/> 
    <beans:property name = "jpaVendorAdapter"> 
    <beans:bean class = "org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"/> 
    </beans:property> 
    <beans:property name = "packagesToScan" value = "com.contact.list.form" /> 
    <beans:property name="jpaProperties"> 
    <beans:props> 
     <beans:prop key="hibernate.dialect">org.hibernate.dialect.PostgreSQLDialect</beans:prop> 
     <beans:prop key = "hibernate.show_sql">true</beans:prop> 
     <beans:prop key="hibernate.hbm2ddl.auto">update</beans:prop> 
    </beans:props> 
    </beans:property> 
</beans:bean> 

<beans:bean id = "passwordEncoder" class = "org.springframework.security.authentication.encoding.Md5PasswordEncoder"/> 



<context:annotation-config/> 

<!-- JPA Config --> 

<!-- JPA Repository Abstraction Config --> 

<jpa:repositories base-package="com.contact.list.repository" entity-manager-factory-ref="emf" transaction-manager-ref="transactionManager"/> 

printemps-security.xml

<http auto-config="true" use-expressions="true"> 
<intercept-url pattern="/home*" access="hasRole('ROLE_USER')"/> 
    <form-login login-page="/login" default-target-url="/home" authentication-failure-url="/loginfailed" /> 
    <logout logout-success-url="/logout" /> 
    </http> 


    <authentication-manager> 
    <authentication-provider user-service-ref = "customUserDetailsService"> 
    <password-encoder ref = "passwordEncoder"/> 
    </authentication-provider> 
    </authentication-manager> 

    <beans:bean id="customUserDetailsService" class="com.contact.list.service.CustomUserDetailsService"/> 

    <beans:bean id = "passwordEncoder" class = "org.springframework.security.authentication.encoding.Md5PasswordEncoder"/> 

    </beans:beans> 
+0

est '@ Autowired' travaille dans toute autre partie de votre demande? Avez-vous '' ou '' dans votre XML? –

+0

Peut-être votre bean "UserService" est défini dans un enfant ApplicationContext (servlet-context.xml) et spring-security.xml est chargé dans un parent ApplicationContext (contexte racine, chargé par ContextLoaderListener) – Luciano

+0

@Tomasz Nurkiewicz: Oui Autowire fonctionne dans d'autres parties de l'application. Et j'ai ce qui suit dans mon xml: -> Tous les paquets sont à l'intérieur. Et oui, j'ai aussi dans mon xml. –

Répondre

3

Ainsi, pour l'écrire comme une réponse, ce qui est arrivé ici est que dans une application web typique de printemps, vous avez le contexte d'application (terminologie de printemps , voici où les haricots vivent) qui appartient à la servlet Spring MVC. Ce fichier est défini dans le fichier web.xml sous la forme /WEB-INF/spring/appServlet/servlet-context.xml. D'autre part, le filtre de sécurité Spring ne peut pas accéder à un tel contexte, il ne peut accéder qu'au contexte racine. Le contexte racine est chargé avec le ContextLoaderListener et les haricots définis dans:

<param-value> 
    /WEB-INF/spring/root-context.xml 
    /WEB-INF/spring-security.xml 
</param-value> 

En tant que Racine de contexte a été défini, le contexte Servlet est construit comme un enfant du contexte racine. Cela permet au contexte de servlet d'accéder aux beans qui vivent dans son parent, mais l'inverse n'est pas possible. Ensuite, les beans de base en tant que source de données, le système de persistance (JPA) et les services ont été définis dans le contexte de servlet.Le système de sécurité tentait d'accéder à un bean service (le service utilisateur) sans succès car ce service se trouvait dans le contexte Servlet au lieu du contexte racine (où réside Spring Security), d'où l'exception Null Pointer.

Solution: Déplacer les beans Datasource, JPA et Services vers le contexte racine et laisser le contexte de servlet pour les contrôleurs et vues Spring MVC.

+0

Un commentaire: Dans Servlet 3.0, où vous pouvez définir vos servlets et filtres en utilisant Java au lieu du fichier web.xml, vous pouvez tout mettre ensemble dans un seul contexte d'application. – Luciano

0

Vous devez supprimer l'annotation @Repository de la classe UserServiceImpl. Sinon Spring essaie de instancie le haricot deux fois (à cause de la @Service et l'annotation @Repository) ...

@Service 
@Repository 
@Transactional 
public class UserServiceImpl implements UserService {