4

我是Spring新手,面臨Spring Security的問題。Spring Security 3.1 + JPA - 空指針異常

我想實現一個自定義UserDetailsS​​ervice用戶檢索和獲取空指針異常,當我訪問UserService對象。我正在自動裝配這個對象。當其他控制器和服務方法完成時,autowirng工作正常,但由於某種原因,它不在這裏工作,因此當訪問自動裝配對象(UserService)時,我得到空指針異常。

我真的很感激這方面的幫助。

異常堆棧跟蹤:

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) 

CustomUserDetailsS​​ervice類別:

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接口:

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實現類:

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

} 

用戶類別:

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

彈簧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

「@ Autowired」是否在您的應用程序的任何其他部分工作?你的XML中是否有''或''? –

+0

也許你的「UserService」bean是在一個子ApplicationContext(servlet-context.xml)中定義的,並且spring-security.xml被加載到一個父ApplicationContext中(又稱根上下文,由ContextLoaderListener加載) – Luciano

+0

@Tomasz Nurkiewicz:Yes Autowire正在工作在應用程序的其他部分。我在我的XML中有以下內容: - >所有mypackages都在這裏面。是的,我也有我的XML中的。 –

回答

3

所以,把它寫一個答案,這裏發生的事情是,在典型的Spring Web應用程序,您必須在應用程序上下文(春季術語,這裏是屬於Spring MVC Servlet的beans live)。

這個在web.xml中定義爲/WEB-INF/spring/appServlet/servlet-context.xml

另一方面,Spring Security Filter不能訪問這樣的上下文,它只能訪問根上下文。 根上下文裝有ContextLoaderListener和定義的豆類:

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

爲根上下文的定義,servlet上下文被構建爲根上下文的孩子。這使Servlet上下文能夠訪問其父代中的bean,但另一種方式是不可能的。

然後,在Servlet上下文中定義了作爲DataSource,Persistence系統(JPA)和Services的基本bean。安全系統嘗試訪問服務bean(用戶服務)時失敗,因爲此服務位於Servlet上下文中,而不是Root Context(Spring Security所在的位置),因此爲空指針異常。

解決方案:將數據源,JPA和服務bean移動到根上下文中,併爲Spring MVC控制器和視圖保留Servlet上下文。

+0

評論:在Servlet 3.0中,您可以使用Java而不是web.xml文件定義您的Servlet和過濾器,您可以將所有內容放在一個應用程序上下文中。 – Luciano

0

您應該從UserServiceImpl類中刪除@Repository註釋。否則,Spring會嘗試實例化bean兩次(因爲@Service和@Repository註釋)...

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