Spring Security - Customizando UserServiceDetails

olá, viventes.

gostaria de saber se algum dos amigos tem alguma ideia sobre o que está acontecendo.

estou usando o spring security na minha aplicação web com customização do UserServiceDetails.
quando inicio a aplicação, nenhuma exceção é lançada. porém quando tento acessar a página de login, a seguinte exceção é lançada e a página de login não está acessível:

16:51:37,109 DEBUG XmlWebApplicationContext:272 - Publishing event in context [org.springframework.web.context.support.XmlWebApplicationContext@172290f]: org.springframework.security.event.authorization.AuthenticationCredentialsNotFoundEvent[source=FilterInvocation: URL: /login/index.jsp]
16:51:37,109 DEBUG ExceptionTranslationFilter:150 - Authentication exception occurred; redirecting to authentication entry point
org.springframework.security.AuthenticationCredentialsNotFoundException: An Authentication object was not found in the SecurityContext
	at org.springframework.security.intercept.AbstractSecurityInterceptor.credentialsNotFound(AbstractSecurityInterceptor.java:342)
	at org.springframework.security.intercept.AbstractSecurityInterceptor.beforeInvocation(AbstractSecurityInterceptor.java:254)
	at org.springframework.security.intercept.web.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:106)
	at org.springframework.security.intercept.web.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:83)
	at org.springframework.security.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:390)
(...)
16:51:37,110 DEBUG ExceptionTranslationFilter:200 - Authentication entry point being called; SavedRequest added to Session: SavedRequest[http://localhost:8085/simecnet2.0/login/index.jsp]

mensagem exibida no firefox

Firefox has detected that the server is redirecting the request for this address in a way that will never complete.

web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app id="WebApp_ID" version="2.5"
	xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
	<description>SimecNET</description>
	<display-name>simecnet2.0</display-name>
	<context-param>
		<param-name>javax.faces.DEFAULT_SUFFIX</param-name>
		<param-value>.xhtml</param-value>
	</context-param>
	<context-param>
		<param-name>com.sun.faces.enableRestoreView11Compatibility</param-name>
		<param-value>true</param-value>
	</context-param>
	<context-param>
		<param-name>org.ajax4jsf.handleViewExpiredOnClient</param-name>
		<param-value>true</param-value>
	</context-param>
	<context-param>
		<param-name>org.richfaces.CONTROL_SKINNING</param-name>
		<param-value>enable</param-value>
	</context-param>
	<context-param>
		<param-name>org.richfaces.CONTROL_SKINNING_CLASSES</param-name>
		<param-value>enable</param-value>
	</context-param>
	<context-param>
		<param-name>org.richfaces.CONTROL_SKINNING_LEVEL</param-name>
		<param-value>extended</param-value>
	</context-param>
	<context-param>
		<param-name>javax.faces.STATE_SAVING_METHOD</param-name>
		<param-value>client</param-value>
	</context-param>

	<context-param>
		<param-name>org.ajax4jsf.COMPRESS_SCRIPT</param-name>
		<param-value>false</param-value>
	</context-param>

	<context-param>
		<param-name>org.richfaces.LoadScriptStrategy</param-name>
		<param-value>ALL</param-value>
	</context-param>

	<context-param>
		<param-name>org.richfaces.LoadStyleStrategy</param-name>
		<param-value>ALL</param-value>
	</context-param>

	<context-param>
		<param-name>contextConfigLocation</param-name>
		<param-value>
                         /WEB-INF/applicationContext.xml
                         /WEB-INF/applicationContext-security.xml
                </param-value>
	</context-param>
	
	<context-param>
		<param-name>javax.faces.CONFIG_FILES</param-name>
		<param-value>/WEB-INF/faces-config.xml</param-value>
	</context-param>

	<filter>
		<display-name>Ajax4jsf Filter</display-name>
		<filter-name>ajax4jsf</filter-name>
		<filter-class>org.ajax4jsf.Filter</filter-class>
		<init-param>
			<param-name>createTempFiles</param-name>
			<param-value>false</param-value>
		</init-param>
		<init-param>
			<param-name>maxRequestSize</param-name>
			<param-value>104857600</param-value>
		</init-param>
	</filter>
	
	<filter>
		<filter-name>hibernateFilter</filter-name>
		<filter-class>org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter</filter-class>
	</filter>
	
	<filter>
		<filter-name>SessionTimeoutFilter</filter-name>
		<filter-class>br.com.unisistemas.simecnet.view.filter.SessionTimeoutFilter</filter-class>
	</filter>
	
	<filter-mapping>
		<filter-name>ajax4jsf</filter-name>
		<servlet-name>Faces Servlet</servlet-name>
		<dispatcher>REQUEST</dispatcher>
		<dispatcher>FORWARD</dispatcher>
		<dispatcher>INCLUDE</dispatcher>
	</filter-mapping>

	<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.web.context.request.RequestContextListener</listener-class>
	</listener>
	<listener>
		<listener-class>br.com.unisistemas.simecnet.view.listener.SessionListener</listener-class>
	</listener>
	<listener>
		<listener-class>org.springframework.security.ui.session.HttpSessionEventPublisher</listener-class>
	</listener>

	<servlet>
		<servlet-name>Faces Servlet</servlet-name>
		<servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
		<load-on-startup>1</load-on-startup>
	</servlet>

	<servlet-mapping>
		<servlet-name>Faces Servlet</servlet-name>
		<url-pattern>*.jsf</url-pattern>
	</servlet-mapping>

	<session-config>
		<session-timeout>10</session-timeout>
	</session-config>
	<welcome-file-list>
		<welcome-file>/index.jsp</welcome-file>
	</welcome-file-list>
	<login-config>
		<auth-method>BASIC</auth-method>
	</login-config>
</web-app>

applicationContext.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:context="http://www.springframework.org/schema/context"
	xmlns:tx="http://www.springframework.org/schema/tx"
	xsi:schemaLocation="http://www.springframework.org/schema/beans 
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context-2.5.xsd"
	default-autowire="byName" default-lazy-init="false">

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

	<context:property-placeholder location="WEB-INF/hibernate.properties" />

	<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
		destroy-method="close">
		<property name="driverClassName" value="${hibernate.connection.driver_class}" />
		<property name="url" value="${hibernate.connection.url}" />
		<property name="username" value="${hibernate.connection.username}" />
		<property name="password" value="${hibernate.connection.password}" />
		<property name="initialSize" value="0" />
		<property name="poolPreparedStatements" value="false" />
		<property name="defaultAutoCommit" value="true" />
		<property name="defaultTransactionIsolation" value="2" />
		<property name="maxActive" value="200" />
		<property name="maxIdle" value="20" />
		<property name="minIdle" value="10" />
		<property name="minEvictableIdleTimeMillis" value="300000" />
		<property name="maxWait" value="200" />

	</bean>

	<bean id="authenticationManager" class="org.springframework.security.providers.ProviderManager">
		<property name="sessionController" ref="singleSession" />
		<property name="providers">
			<list>
				<ref bean="authenticationProvider" />
			</list>
		</property>
	</bean>

	<bean id="authenticationProvider"
		class="org.springframework.security.providers.dao.DaoAuthenticationProvider">
		<property name="userDetailsService" ref="userDetailService" />
	</bean>

	<bean id="userDetailService"
		class="br.com.unisistemas.simecnet.dao.impl.MyUserDetailsService">
	</bean>

	<bean id="sessionFactory"
		class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
		<property name="dataSource" ref="dataSource" />
		<property name="configLocation" value="classpath:hibernate.cfg.xml" />
		<property name="configurationClass" value="org.hibernate.cfg.AnnotationConfiguration" />
	</bean>

	<bean id="sessionRegistry"
		class="org.springframework.security.concurrent.SessionRegistryImpl" />

	<bean id="singleSession"
		class="org.springframework.security.concurrent.ConcurrentSessionControllerImpl">
		<property name="maximumSessions" value="1" />
		<property name="exceptionIfMaximumExceeded" value="true" />
		<property name="sessionRegistry" ref="sessionRegistry" />
	</bean>

</beans>

applicationContext-security.xml

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

<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-2.0.xsd
                http://www.springframework.org/schema/security
                http://www.springframework.org/schema/security/spring-security-2.0.1.xsd">

	<global-method-security secured-annotations="enabled">
	</global-method-security>

	<http>

		<!-- Don't set any role restrictions on login.jsp -->
		<intercept-url pattern="/login/index.jsp" access="IS_AUTHENTICATED_ANONYMOUSLY" />
		<intercept-url pattern="/index.jsp" access="IS_AUTHENTICATED_ANONYMOUSLY" />
		<intercept-url pattern="/image/*" access="IS_AUTHENTICATED_ANONYMOUSLY" />
		<intercept-url pattern="/css/*" access="IS_AUTHENTICATED_ANONYMOUSLY" />

		<!-- Restrict access to ALL other pages -->
		<intercept-url pattern="/**" access="ROLE_USER" />

		<!-- Set the login page and what to do if login fails -->
		<form-login login-page="/login/index.jsp"
			authentication-failure-url="/login/index.jsp?login_error=1"
			always-use-default-target="true" default-target-url="/start/start.jsf" />

		<concurrent-session-control max-sessions="1"
			exception-if-maximum-exceeded="true" />
	</http>

	<authentication-provider user-service-ref="userDetailService">
	</authentication-provider>
	
</beans:beans>

MyUserDetailsService

@Component("MyUserDetailsService")
public class MyUserDetailsService implements UserDetailsService {
    
    @Resource(name = "NetContaAcessoDAO")
    private NetContaAcessoDAO netContaAcessoDAO;
    
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException, DataAccessException {

        NetContaAcesso netContaAcesso = netContaAcessoDAO.find(Integer.parseInt(username));
        
        // TODO accountNonExpired, credentialsNonExpired, accountNonLocked,
        // getAuthorities
        User user = new User(netContaAcesso.getContaCodigo().toString(), netContaAcesso.getSenha().trim(), true, true,
                true, true, getAuthorities(false));
        return user;
    }
    
    private GrantedAuthority[] getAuthorities(boolean isAdmin) {

        List<GrantedAuthority> authList = new ArrayList<GrantedAuthority>(2);
        authList.add(new GrantedAuthorityImpl("ROLE_USER"));
        if (isAdmin) {
            authList.add(new GrantedAuthorityImpl("ROLE_ADMIN"));
        }
        return authList.toArray(new GrantedAuthority[] {});
    }
    
    public void setNetContaAcessoDAO(NetContaAcessoDAO netContaAcessoDAO) {

        this.netContaAcessoDAO = netContaAcessoDAO;
    }
    
    public void setSessionInfo(SessionInfo sessionInfo) {

        this.sessionInfo = sessionInfo;
    }
}

Achei o problema: como estou usando Spring IoC junto com o Spring Security, o bean que eu implementei meu UserServiceDetails estava cadastrado duas vezes: uma pelo @Service e outra no applicationContext.
Acredito que o Spring somente descobria essa duplicação de cadastro de bean quando esse bean era chamado.

Bastou retirar do meu applicationContext o cadastro do bean e usar no applicationContext-security

	<authentication-provider user-service-ref="MyUserDetailsService" />

onde MyUserDetailsService é o nome que cadastrei em @Service(“MyUserDetailsService”).