2011-05-07 125 views
16

我正在使用Spring 3和Spring Security开发项目。我的问题是与IoC容器。当我为Spring Security-3编写自己的UserDetailsService实现时,问题就开始了。我检查了其他问题,但仍无法解决问题。问题的@Autowired对象在一个类中获得一个空值,而在另一个类中成功连接

定义是:

我有两个单独的类(一个是UsersController.java延伸@Controller,和ProjectUserDetailsService延伸@Service),其使用作为自动连接的共同对象。但虽然对象在UsersController中成功自动装配,但在ProjectUserDetailsService类中为null,尽管此类的对象(ProjectUserDetailsService)已成功创建(我通过调试进行了验证)。

任何建议如何解决这个问题?

这里是我的web.xml,project-servlet.xmlproject-security.xml文件和相关的类。

Web.xml`

<?xml version="1.0" encoding="UTF-8"?> 
<!-- 
    - Tutorial web application 
    - 
    --> 
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" version="2.5"> 
    <display-name>Ecognitio with Spring Security</display-name> 
    <context-param> 
    <param-name>contextConfigLocation</param-name> 
    <param-value> 
      /WEB-INF/ecognitio-servlet.xml 
      /WEB-INF/ecognitio-security.xml 
     </param-value> 
    </context-param> 
    <context-param> 
    <param-name>webAppRootKey</param-name> 
    <param-value>tutorial.root</param-value> 
    </context-param> 
    <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> 
    <listener> 
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> 
    </listener> 
    <listener> 
    <listener-class>org.springframework.security.web.session.HttpSessionEventPublisher</listener-class> 
    </listener> 
    <servlet> 
    <servlet-name>ecognitio</servlet-name> 
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> 
    <load-on-startup>1</load-on-startup> 
    </servlet> 
    <servlet-mapping> 
    <servlet-name>project</servlet-name> 
    <url-pattern>*.action</url-pattern> 
    </servlet-mapping> 
    <servlet-mapping> 
    <servlet-name>project</servlet-name> 
    <url-pattern>*.html</url-pattern> 
    </servlet-mapping> 
    <welcome-file-list> 
    <welcome-file>index.jsp</welcome-file> 
    </welcome-file-list> 
</web-app> 

project-servlet.xml

<?xml version="1.0" encoding="UTF-8"?> 

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

    <!-- Scans the classpath of this application for @Components to deploy as beans --> 
    <context:component-scan base-package="com.project" /> 

    <!-- Configures the @Controller programming model --> 
    <mvc:annotation-driven /> 

    <bean id="messageSource" 
      class="org.springframework.context.support.ResourceBundleMessageSource" 
      p:basename="Messages"/> 

    <!-- misc --> 
<!-- <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"> 
     <property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/> 
     <property name="suffix" value=".jsp"/> 
    </bean> --> 


    <bean id="viewResolver" 
     class="org.springframework.web.servlet.view.UrlBasedViewResolver"> 

     <property name="viewClass"> 
     <value> 
       org.springframework.web.servlet.view.tiles2.TilesView 
      </value> 
     </property> 
    </bean> 

    <bean id="tilesConfigurer" 
    class="org.springframework.web.servlet.view.tiles2.TilesConfigurer"> 
     <property name="definitions"> 
      <list> 
       <value>/WEB-INF/tiles.xml</value> 
      </list> 
     </property> 
    </bean> 

    <!-- Configures Hibernate - Database Config --> 
    <import resource="db-config.xml" /> 
</beans> 

project-security.xml

<?xml version="1.0" encoding="UTF-8"?> 

<!-- 
    - Sample namespace-based configuration 
    - 
    --> 

<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.1.xsd"> 

    <debug /> 

    <global-method-security pre-post-annotations="enabled"> 
     <!-- AspectJ pointcut expression that locates our "post" method and applies security that way 
     <protect-pointcut expression="execution(* bigbank.*Service.post*(..))" access="ROLE_TELLER"/> 
     --> 
    </global-method-security> 

    <http pattern="/loggedout.jsp" security="none"/> 

    <http use-expressions="true" > 
     <intercept-url pattern="/secure/extreme/**" access="hasRole('ROLE_SUPERVISOR')"/> 
     <intercept-url pattern="/secure/**" access="isAuthenticated()" /> 

     <!-- 
      Allow all other requests. In a real application you should 
      adopt a whitelisting approach where access is not allowed by default 
      --> 
     <intercept-url pattern="/login.jsp*" access="isAuthenticated()==false"/> 
     <intercept-url pattern="/timeout.jsp*" access="isAuthenticated()==false"/> 
     <intercept-url pattern="/**" access="hasRole('ROLE_USER')" /> 

     <!-- <intercept-url pattern="/**" access="permitAll" /> --> 
     <form-login login-page="/login.jsp" authentication-failure-url="/login.jsp?login_error=1" default-target-url="/dashboard.html" /> 
     <logout logout-success-url="/login.jsp" delete-cookies="JSESSIONID"/> 
     <remember-me /> 
<!-- 
    Uncomment to enable X509 client authentication support 
     <x509 /> 
--> 
     <!-- Uncomment to limit the number of sessions a user can have 
     <session-management invalid-session-url="/login.jsp"> 
      <concurrency-control max-sessions="1" error-if-maximum-exceeded="true" /> 
     </session-management> 
     --> 

    </http> 

      <!-- HERE IS WHERE I USE an object of ProjectUserDetailsService --> 
    <authentication-manager> 
     <authentication-provider user-service-ref="userDetailsService" /> 
    </authentication-manager> 


<!-- 

</beans:beans> 

UsersController.java(UsersDAO类的对象的自动装配成功该类)

package com.project.users; 

//required imports 



@Controller 
public class UsersControllers 
{ 

@Autowired 
private UsersDAO usersDAO; 

    //Some more autowires and some class specific code 


} 

ProjectUserDetailsService.java(其中UsersDAO的自动装配不工作)

package com.project.security; 

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

//required imports 


@SuppressWarnings("deprecation") 
@Service("userDetailsService") 
public class ProjectUserDetailsService implements UserDetailsService { 

    @Autowired 
    private UsersDAO usersDAO; 
    @Autowired private Assembler assembler; 

    @Transactional(readOnly = true) 
    public UserDetails loadUserByUsername(String username) 
     throws UsernameNotFoundException, DataAccessException { 

    UserDetails userDetails = null; 
    //For debugging purposes 
    if(usersDAO==null){ 
     System.out.println("DAO IS NULL"); 
     System.out.println("DAO IS NULL"); 
     System.out.println("DAO IS NULL"); 

    } 
    User userEntity = usersDAO.findUserbyEmail("'"+username+"'"); 


    if (userEntity == null) 
     throw new UsernameNotFoundException("user not found"); 

    return assembler.buildUserFromUserEntity(userEntity); 

    } 
} 
+1

我在'project-security.xml'中看不到任何引用到'ProjectUserDetailsS​​ervice'。它是否丢失? – skaffman 2011-05-07 14:41:07

+0

由于ProjectUserDetailsS​​ervice.java被定义为@Service(“userDetailsS​​ervice”),因此在引导时会创建一个具有该名称的bean。所以这不是问题。 – 2011-05-07 21:37:00

回答

1

由于第二豆不是如在project-servlet.xml指定的指定豆注释封装元件扫描:

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

它确实不认为它是一项服务,不会翻译注释。

您需要进一步扩展,或将其移动到一个包开始com.project否则这样的:

<context:component-scan base-package="com" /> 
+0

对不起,我打错了。 ProjectUserDetailsS​​ervice.java实际上是在com.project包中。所以这不是问题。创建一个名为userDetailsS​​ervice(ProjectUserDetailsS​​ervice)的bean,但其依赖性不是自动装配的。我纠正了这个问题。 – 2011-05-07 21:38:42

4

是的,这似乎是不可能的自动装配对象成从春季安全类继承豆。我不知道这是在春季安全漏洞,或者如果它为安全而做,或者如果任何人有解释我会有兴趣听到它。您可以通过xml配置手动注入bean来解决问题(与使用@Autowired注释相反),然后它们将出现。尽管一个字的谨慎..

我做到了这一点,我注意到我的userDao有注释(特别是@Transactional)不再在事务中操作。我的userDao在多个地方使用。如果我将它注入到我的自定义AbstractUserDetailsAuthenticationProvider中,它不再在任何其他使用它的类的事务中运行。当被其他接收它的对象(通过@Autowired或手动xml注入)使用时,移除注入到自定义AbstractUserDetailsAuthenticationProvider的事件将事务功能恢复到我的userDao。

那么我是如何让我的userDao进入我的Spring Security环境并仍然保留@Transactional的呢?我不得不创建一个工厂类:

public class UserDaoFactory { 

private static UserDao userDao; 

public static UserDao getUserDao() { 
    return UserDaoFactory.userDao; 
} 

public void setUserDao(UserDao userDao) { 
    UserDaoFactory.userDao = userDao; 
} 
} 

然后把这个和你的DAO两个对象进入春季容器:

<bean id="userDao" class="com.package.UserDaoImpl"> 
    <property name="sessionFactory" ref="sessionFactory" /> 
</bean> 

<bean id="userDaoFactory" class="com.package.UserDaoFactory"> 
    <property name="userDao" ref="userDao" /> 
</bean> 

所以userDAO的会得到自动连接到您的userDaoFactory。它将拥有@Transactional的所有能力(因为春季安全还没有剥离它?)。然后在你的春季安全对象,你可以做:

userDao = UserDaoFactory.getUserDao(); 

我初始化期间实施ServletContextAware在我的自定义AbstractUserDetailsAuthenticationProvider对象上面做一次,中提琴。

因此,请注意,尽管您可以通过xml配置手动将bean注入Spring安全对象以克服@Autowired问题,但如果您试图在@Transactional中封装该DAO,最终会出现新问题。

现在可能有人知道为什么会发生这种情况。这可能是我的错误配置(我承认我不是春季专家),或者它可能是春季的一个特征。我很想听听有什么人说,以及如何改善这一点。

0

这可能是由于SEC-1911,当依赖像AutowiredAnnotationBeanPostProcessor这样的BeanPostProcessors和使用<debug />元素时导致问题。尝试删除<debug />并查看@Autowired是否再次运行。请注意,这第一个问题是SEC-1885的重复,这是固定的,但第一个问题的症状更好地匹配此问题。

-1

我有同样的问题。虽然这可能以多种方式发生,但在我的情况下,我正在创建一个新对象,而不是使用autowired之一。代替即:

private Service service = new ServiceImpl(); 

@Autowired private Service service; 

这不是在使用资源库注射的服务,但在最重要的是一个控制器。

相关问题