简体   繁体   English

Spring Security 3.1 + JPA - 空指针异常

[英]Spring Security 3.1 + JPA - Null pointer exception

I am a Spring newbie and facing issues with Spring Security. 我是Spring新手,面临Spring Security的问题。

I am trying to implement a custom UserDetailsService for user retrieval and getting a null pointer exception when I access the UserService object. 我正在尝试实现自定义UserDetailsS​​ervice以进行用户检索,并在访问UserService对象时获取空指针异常。 I am autowiring this object. 我正在自动装配这个对象。 The autowirng is working fine when done on other Controller and Service methods but for some reason, its not working here and hence I am getting the null pointer exception when the autowired object(UserService) is accessed. 当在其他Controller和Service方法上完成时,autowirng工作正常但由于某种原因,它在这里不起作用,因此当访问自动对象(UserService)时,我得到空指针异常。

I would really appreciate help on this. 我真的很感激这方面的帮助。

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

spring-security.xml 弹簧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>

So, to write it as an answer, what happened here is that in a typical Spring web application, you have the application context (Spring terminology, here is where beans live ) that belongs to the Spring MVC Servlet. 所以,把它写一个答案,这里发生的事情是,在典型的Spring Web应用程序,您必须在应用程序上下文(春季术语,这里是豆 )属于Spring MVC的Servlet的。

This one is defined in the web.xml as /WEB-INF/spring/appServlet/servlet-context.xml . 这个在web.xml中定义为/WEB-INF/spring/appServlet/servlet-context.xml

On the other hand, the Spring Security Filter cannot access such context, it can only access the Root Context. 另一方面,Spring Security Filter无法访问此类上下文,它只能访问Root Context。 The Root Context is loaded with the ContextLoaderListener and the beans defined in: 根上下文加载ContextLoaderListener和bean中定义的bean:

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

As a Root Context was defined, the Servlet Context is built as a child of the Root Context. 在定义根上下文时,Servlet上下文被构建为根上下文的子级。 This enables the Servlet Context to access the beans that live in its parent, but the other way is not possible. 这使Servlet Context能够访问其父级中的bean,但另一种方式是不可能的。

Then, basic beans as the DataSource, the Persistence system (JPA) and Services were defined in the Servlet Context. 然后,在Servlet上下文中定义了基本bean作为DataSource,Persistence系统(JPA)和Services。 The security system was trying to access a service bean (the User Service) without success as this service was located in the Servlet Context instead of the Root Context (where Spring Security lives), hence the Null Pointer Exception. 安全系统试图访问服务bean(用户服务)但没有成功,因为此服务位于Servlet Context而不是Root Context(Spring Security所在的位置),因此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. 解决方案:将数据源,JPA和服务bean移动到根上下文,并保留Spring MVC控制器和视图的Servlet上下文。

You should remove the @Repository annotation from the UserServiceImpl class. 您应该从UserServiceImpl类中删除@Repository注释。 Otherwise Spring tries to instantiates the bean twice (because of the @Service and the @Repository annotation) ... 否则,Spring会尝试两次实例化bean(因为@Service和@Repository注释)...

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM