2011-05-07 14 views
16

Estoy trabajando en un proyecto utilizando Spring 3 y Spring Security. Mi problema es con el contenedor IoC. El problema comenzó cuando escribí mi propia implementación de UserDetailsService para Spring Security-3. Revisé las otras preguntas pero todavía no pude resolver el problema.@El objeto cableado obtiene un valor nulo en una clase, mientras está correctamente conectado en otra

Definición del problema es:

Tengo dos clases separadas (uno es UsersController.java que se extiende @Controller, y ProjectUserDetailsService que se extiende @Service) que utiliza un objeto común que se autowired. Pero mientras el objeto se autocableó correctamente en UsersController, es null en la clase ProjectUserDetailsService aunque el objeto de esta clase (ProjectUserDetailsService) se ha creado correctamente (lo verifiqué mediante la depuración).

¿Alguna sugerencia de cómo solucionar esto?

Aquí están mis archivos web.xml, project-servlet.xml y project-security.xml y clases relacionadas.

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 (autowiring de un objeto de clase UsersDAO es exitoso para esta clase)

package com.project.users; 

//required imports 



@Controller 
public class UsersControllers 
{ 

@Autowired 
private UsersDAO usersDAO; 

    //Some more autowires and some class specific code 


} 

ProjectUserDetailsService.java (donde autowiring de UsersDAO no funciona)

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

No veo ninguna referencia en 'project-security.xml' a' ProjectUserDetailsService'. ¿Está perdido? – skaffman

+0

Dado que ProjectUserDetailsService.java se define como @Service ("userDetailsService"), se crea un bean con este nombre en bootstrap. Entonces este no es el problema. –

Respuesta

1

Desde la segunda bean no está en la exploración componente de envase anotación bean especificado como se especifica en project-servlet.xml:

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

lo hace no lo considera como un servicio y no traduce las anotaciones.

necesita ampliar aún más o moverlo a un paquete a partir de com.project o bien como esto:

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

Lo siento, había escrito mal. ProjectUserDetailsService.java está realmente en el paquete com.project. Entonces este no es el problema. Se crea un bean con el nombre userDetailsService (de ProjectUserDetailsService) pero su dependencia no está autoconectada. Corregí la pregunta. –

4

sí, parece ser imposible Autowire objetos en los granos que heredan de las clases de seguridad de la primavera. no sé si esto es un error en la seguridad de primavera, o si es hecho por seguridad o qué. si alguien tiene una explicación Estoy interesado en escucharlo. Sin embargo, puede resolver su problema inyectando manualmente los beans a través de la configuración xml (en lugar de usar la anotación @Autowired) y luego estarán presentes. Sin embargo, una palabra de precaución ...

Lo hice y noté que mi userDao, que tenía anotaciones en ella (específicamente @Transactional), ya no estaba operando en una transacción.Mi usuario Dao estaba siendo usado en múltiples lugares. Sin embargo, si lo inyecté en mi AbstractUserDetailsAuthenticationProvider personalizado, ya no operaba en una transacción para ninguna otra clase que lo usara. Al eliminar la inyección en el CustomUserDetailsAuthenticationProvider personalizado, restauré la funcionalidad de transacción a mi userDao cuando la usaban otros objetos que la recibían (ya sea mediante @Autowired o inyección manual xml).

Entonces, ¿cómo conseguí mi userDao en mi contexto Spring Security y todavía lo guardo @Transactional? Tuve que crear una clase de fábrica:

public class UserDaoFactory { 

private static UserDao userDao; 

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

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

luego poner esto y sus dao dos objetos en el contenedor de primavera:

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

Así que la UserDAO obtendrá autowired en su userDaoFactory. Tendrá toda su capacidad @Transactional (¿porque la seguridad de primavera no la ha quitado?). Luego, en el objeto de seguridad de resorte que puede hacer un:

userDao = UserDaoFactory.getUserDao(); 

he implementado ServletContextAware en mi costumbre AbstractUserDetailsAuthenticationProvider objeto de hacer lo anterior una vez durante la inicialización, y la viola.

Tenga en cuenta que, si bien puede inyectar manualmente su bean a través de la configuración xml en el objeto de seguridad de primavera para solucionar el problema de @Autowired, terminará con un nuevo problema si está tratando de ajustar ese DAO en @Transactional.

ahora tal vez alguien sepa por qué esto puede estar pasando. podría ser una mala configuración de mi parte (admito que no soy un experto en primavera), o podría ser una característica de la primavera. Sin embargo, me encantaría escuchar lo que alguien tiene que decir y cómo mejorarlo.

0

Esto es probable debido a SEC-1911 que causa problemas al confiar en BeanPostProcessors como AutowiredAnnotationBeanPostProcessor y usar el elemento <debug />. Intente eliminar <debug /> y vea si @Autowired funciona nuevamente. Tenga en cuenta que este primer problema es un duplicado de SEC-1885, que es donde esto se soluciona, pero los síntomas del primer problema coinciden mejor con este problema.

-1

Tuve el mismo problema. Aunque esto puede suceder de muchas maneras, en mi caso estaba creando un objeto nuevo, en lugar de usar el autowired. es decir .:

private Service service = new ServiceImpl(); 

en lugar de:

@Autowired private Service service; 

Esto no estaba en el servicio mediante la inyección de repositorio, pero en un controlador por encima de eso.

Cuestiones relacionadas