Can I chain multiple instances of AuthenticationEntryPoint
in Spring Security 3.2.4?
I attempting to create the following scenario:
- A certain URL is secured with Spring Security
- The
AuthenticationEntryPoint
used isLoginUrlAuthenticationEntryPoint
- An admin interface can spawn services under this URL
- The admin can choose to secure these services with
CLIENT-CERT
When a user attempts to access the secure URL:
- If the path has been secured with
CLIENT-CERT
then authentication fails unless they have provided a valid certificate the corresponds to a user in theUserService
. Standard Spring Securityx509
authentication. - Once the user has been authentication as per the first point, or if the URL is not secured with
CLIENT-CERT
, they are directed to aFORM
based authentication page. - Once they successfully authenticate with a username and password, they are directed to a landing page.
I am running on Tomcat 7.0.54 with clientAuth="want"
. This works perfectly in a "simple" Spring Security set up - i.e. with one WebSecurityConfigurerAdapter
set to x509()
and another set to formLogin()
as per this example
So, I want a process flow something like the following:
I have had some success with dynamically changing the used authentication method by using a DelegatingAuthenticationEntryPoint
but:
- When using an
AntPathRequestMatcher
to map, say,/form/**
to aLoginUrlAuthenticationEntryPoint
the authentication servlet (/j_spring_security_check
) gives aHTTP404
error. - When using an
AntPathRequestMatcher
to map, say,/cert/**
to aHttp403ForbiddenEntryPoint
the user's details are not extracted from the presented client certificate so this gives aHTTP403
error.
I also cannot see how to force a user to authenticate twice.
I am using the java-config and not XML.
My code:
I have a DelegatingAuthenticationEntryPoint
:
@Bean
public AuthenticationEntryPoint delegatingEntryPoint() {
final LinkedHashMap<RequestMatcher, AuthenticationEntryPoint> map = Maps.newLinkedHashMap();
map.put(new AntPathRequestMatcher("/basic/**"), new BasicAuthenticationEntryPoint());
map.put(new AntPathRequestMatcher("/cert/**"), new Http403ForbiddenEntryPoint());
final DelegatingAuthenticationEntryPoint entryPoint = new DelegatingAuthenticationEntryPoint(map);
entryPoint.setDefaultEntryPoint(new LoginUrlAuthenticationEntryPoint("/login"));
return entryPoint;
}
And my configure
@Override
protected void configure(final HttpSecurity http) throws Exception {
defaultConfig(http)
.headers()
.contentTypeOptions()
.xssProtection()
.cacheControl()
.httpStrictTransportSecurity()
.addHeaderWriter(new XFrameOptionsHeaderWriter(SAMEORIGIN))
.and()
.authorizeRequests()
.accessDecisionManager(decisionManager())
.anyRequest()
.authenticated()
.and()
.httpBasic()
.authenticationEntryPoint(delegatingEntryPoint())
.and()
.sessionManagement()
.maximumSessions(1)
.sessionRegistry(sessionRegistry())
.maxSessionsPreventsLogin(true);
}
Where decisionManager()
returns a UnanimousBased
instance. sessionRegistry()
returns a SessionRegistryImpl
instance. Both methods are @Bean
.
I add a custom UserDetailsService
using:
@Autowired
public void configureAuthManager(
final AuthenticationManagerBuilder authBuilder,
final InMemoryUserDetailsService authService) throws Exception {
authBuilder.userDetailsService(authService);
}
And I have a custom FilterInvocationSecurityMetadataSource
mapped using a BeanPostProcessor
as in this example.