Summary
When you use multiple sec:authentication-managers, you can't @Autowire the AuthenticationEventPublisher because every sec:authentication-manager will instantiate its own public bean
Detailed explanation
I work on a plugin-based authentication architecture. All plugins must be switched by Spring profiles and should coexist if possible (all active have no effect).
Currently I have two kinds of plugins:
- SSO plugins have their own filter and a dedicated authentication manager, e.g.
- For username-password-based plugins (e.g. Active Directory, database, custom lookup) I had to invent an ExtensibleAuthenticationManager that basically wraps a
ProviderManagerand scans forAuthenticationProviders that supportUsernamePasswordToken
public class ExtensibleAuthenticationManager implements AuthenticationManager, InitializingBean
{
private final static Logger log = LogManager.getLogger();
private AuthenticationManager parentAuthenticationManager;
private ProviderManager providerManager;
private Class<? extends Authentication> limitToClass;
@Autowired
private ListableBeanFactory beanFactory;
@Autowired
private MessageSource messageSource;
@Autowired(required = false)
private AuthenticationEventPublisher authenticationEventPublisher;
Actual Behavior
Since Spring Security creates a globally-visible bean of type DefaultAuthenticationEventPublisher every time it creates an authentication manager, you can autowire it
Expected Behavior
There should be either one globally-visible bean of type AuthenticationEventPublisher that can be autowired in other beans if required, or the definition of a new AuthenticationManager should inject an instance of DefaultAuthenticationEventPublisher that is private to the authentication manager.
I would prefer the latter, because if one writes their own security plugin there will be actually no public AuthenticationEventPublisher to Autowire, and developer is forced to find their own way (i.e. declare explicitly) to autowire into their authenticatio components
Configuration
<beans profile="CEDACRISEC" xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:sec="http://www.springframework.org/schema/security"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-4.2.xsd"
>
<sec:authentication-manager id="cedacriAuthenticationManager">
<sec:authentication-provider ref="cedacriAuthenticationProvider"/>
</sec:authentication-manager>
<bean id="formLoginFilter_cedacri" class="it.phoenix.web.security.cedacri.CedacriAuthenticationFilter">
---------------------------
<beans profile="ICCREASEC"
xmlns:sec="http://www.springframework.org/schema/security"
xmlns="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-4.3.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security-4.2.xsd">
<bean id="preAuthFilter_iccrea" class="it.phoenix.web.security.spring.PreAuthenticatedIccreaProcessingFilter">
<property name="sessionAuthenticationStrategy"
ref="org.springframework.security.web.authentication.session.CompositeSessionAuthenticationStrategy#0"/>
<property name="authenticationManager" ref="iccreaAuthenticationManager"/>
<property name="authenticationSuccessHandler" ref="authenticationHandler"/>
<property name="authenticationFailureHandler" ref="authenticationHandler"/>
</bean>
<sec:authentication-manager id="iccreaAuthenticationManager">
<sec:authentication-provider ref="iccreaPreAuthProvider"/>
</sec:authentication-manager>
---------------------------
<beans profile="PREAUTHSEC" xmlns:sec="http://www.springframework.org/schema/security" xmlns="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-4.3.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security-4.2.xsd"
>
<bean id="preAuthFilter_preauthsec" class="it.phoenix.web.security.spring.PreAuthenticatedProcessingFilter">
<property name="sessionAuthenticationStrategy"
ref="org.springframework.security.web.authentication.session.CompositeSessionAuthenticationStrategy#0"/>
<property name="authenticationFailureHandler" ref="authenticationHandler" />
<property name="authenticationSuccessHandler" ref="authenticationHandler" />
<property name="authenticationManager" ref="preauthAuthenticationManager"/>
<property name="principalRequestHeader" value="${httpheader.preauth.user:SM_USER}"/>
<property name="rolesRequestHeader" value="${httpheader.preauth.role:SM_ROLES}"/>
<property name="rolesDelimiter" value="${httpheader.preauth.rolesDelimiter:,}"/>
</bean>
<sec:authentication-manager id="preauthAuthenticationManager">
<sec:authentication-provider ref="profilesAuthenticationProvider"/>
</sec:authentication-manager>
</beans>
---------------------------
<sec:http name="httpSecurityContext" auto-config="false" use-expressions="true" access-decision-manager-ref="accessDecisionManager"
authentication-manager-ref="usernamePasswordAuthenticationManager"
entry-point-ref="authenticationEntryPoint"
>
.............
<bean id="usernamePasswordAuthenticationManager" class="it.phoenix.web.security.spring.ExtensibleAuthenticationManager" lazy-init="true">
<property name="limitToClass" value="org.springframework.security.authentication.UsernamePasswordAuthenticationToken" />
</bean>
</sec:http>
Version
Using version 4.2.11 but should affect all 4.2.x
Related
4400
4269
Comment From: djechelon
Anyway, I implemented a workaround by replacing Autowired with XML property injection of a customized event publisher defined as protected bean
<bean id="usernamePasswordAuthenticationManager" class="it.phoenix.web.security.spring.ExtensibleAuthenticationManager" lazy-init="true">
<property name="limitToClass" value="org.springframework.security.authentication.UsernamePasswordAuthenticationToken"/>
<property name="authenticationEventPublisher">
<bean id="defaultAuthenticationEventPublisher" class="org.springframework.security.authentication.DefaultAuthenticationEventPublisher"/>
</property>
</bean>
Comment From: rwinch
Thanks for the report @djechelon! Would you be willing to submit a PR to fix this?
Comment From: djechelon
@djechelon created a pull request right now. It is an early proposal, without tests (yet)
Comment From: jzheaux
Note that this is the related PR.