简体   繁体   中英

Chained authentication in Spring Security

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 is LoginUrlAuthenticationEntryPoint
  • 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:

  1. 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 the UserService . Standard Spring Security x509 authentication.
  2. 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 a FORM based authentication page.
  3. 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 - ie 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 a LoginUrlAuthenticationEntryPoint the authentication servlet ( /j_spring_security_check ) gives a HTTP404 error.
  • When using an AntPathRequestMatcher to map, say, /cert/** to a Http403ForbiddenEntryPoint the user's details are not extracted from the presented client certificate so this gives a HTTP403 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 .

Chaining multiple entry points won't really work.

Your best option here might be to just customize the form-login process to check for the certificate if it's needed (before authenticating the user). That would probably simplify the configuration overall. It would really just be the same as a normal form-login setup.

The work done by the X509 filter is quite minimal . So for example, you could override the attemptAuthentication method, call super.attemptAuthentication() and then check that the certificate information matches the returned user authentication information.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM