Spring Security 3.1 + JPA - null pointer exception

I am new to Spring and am facing Spring security issues.

I am trying to implement a custom UserDetailsService to fetch a user and get a null pointer exception when accessing a UserService object. I am engaged in autobiography of this object. Autowirng works great when it is done on other Controller and Service methods, but for some reason it does not work here, and therefore I get a null pointer exception when accessing an object with auto-negotiation (UserService).

I would really appreciate help on this.

Exceptional stack trace:

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 Class:

  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 implementation class:

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

User Class:

  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 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"/> 

spring -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> 
+4
source share
2 answers

So, to write this as an answer, what happened here is that in a typical Spring web application, you have the application context (Spring terminology, beans live here) owned by Spring MVC Servlet.

This parameter is defined in the web.xml file as /WEB-INF/spring/appServlet/servlet-context.xml .

On the other hand, Spring's security filter cannot access such a context; it can only access the root context. The root context is loaded using the ContextLoaderListener and beans defined in:

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

As the root context, the servlet context defined as a child of the root context was defined. This allows the servlet context to access the beans that lives in its parent object, but no other way is possible.

Then, the underlying beans as a data source, Persistence System (JPA), and services were defined in the context of the servlet. The security system tried to access the bean service (User Service) without success, because this service was hosted in the servlet context instead of the root context (where Spring Security), hence the Null Pointer exception.

Solution: Move the Datasource, JPA, and Services beans to the root context and leave the servlet context for Spring MVC controllers and views.

+3
source

You must remove the @Repository annotation from the UserServiceImpl class. Otherwise, Spring tries to create a bean twice (due to @Service and @Repository annotations) ...

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

Source: https://habr.com/ru/post/1438728/


All Articles