0

I have a SpringBoot based app, with multiple endpoints. Because of different clients that will be accessing the endpoints I would like to have different Authentication providers protecting them. Some endpoints would be protected by Kerberos (KerberosServiceAuthenticationProvider -- http://docs.spring.io/autorepo/docs/spring-security-kerberos/1.0.0.RC1/reference/htmlsingle/). Some endpoints would be protected by AD/LDAP (ActiveDirectoryLdapAuthenticationProvider).

I currently have it working with Kerberos OR LDAP, but not both:

@Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)
protected class ApplicationSecurity extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {

//For Kerberos
        auth.authenticationProvider(kerberosAuthenticationProvider())
            .authenticationProvider(kerberosServiceAuthenticationProvider());
//For LDAP  
        //auth.authenticationProvider(customAuthenticationProvider());
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
            http
                .authorizeRequests()
                    .antMatchers(HttpMethod.GET, APPLICATION_ADMIN_ENDPOINTS)
                    .permitAll()
                    .and()
                .authorizeRequests()
                    .antMatchers(HttpMethod.PUT, APPLICATION_ADMIN_ENDPOINTS)
                    .hasAnyAuthority(AUTHENTICATED_APPLICATION_ADMIN_AUTHORITIES)
                    .and()
                .authorizeRequests()
                    .antMatchers(HttpMethod.DELETE, APPLICATION_ADMIN_ENDPOINTS)
                    .hasAnyAuthority(AUTHENTICATED_APPLICATION_ADMIN_AUTHORITIES)
                    .and()
                .authorizeRequests()
                    .antMatchers(CLIENT_ENDPOINTS)
                    .permitAll()
                    .and()
                .authorizeRequests()
                    .antMatchers(SWAGGER_ENDPOINTS)
                    .permitAll()
                    .and()
                .authorizeRequests()
                    .antMatchers(MANAGER_ENDPOINTS)
                    .hasAnyAuthority(AUTHENTICATED_MANAGER_AUTHORITIES)
                    .and()
                .authorizeRequests()
                    .antMatchers(TRUSTED_AGENT_ENDPOINTS)
                    .hasAnyAuthority(AUTHENTICATED_TRUSTED_AGENT_AUTHORITIES)
                    .and()
                .authorizeRequests()
                    .antMatchers("/kerb/**")
                    .hasAnyAuthority(AUTHENTICATED_APPLICATION_ADMIN_AUTHORITIES)
                    .and()
                .addFilterBefore(spnegoAuthenticationProcessingFilter(authenticationManagerBean()), BasicAuthenticationFilter.class)
                .httpBasic()
                    .and()
                .csrf()
                    .disable();
    }
}

@Bean
public AuthenticationProvider customAuthenticationProvider() {
    ActiveDirectoryLdapAuthenticationProvider provider = new ActiveDirectoryLdapAuthenticationProvider(
            ldapDomain, ldapUrl);
    SimpleCaseAndWhitespaceMitigatingAuthoritiesMapper authoritiesMapper = new SimpleCaseAndWhitespaceMitigatingAuthoritiesMapper();
    provider.setAuthoritiesMapper(authoritiesMapper);
    provider.setConvertSubErrorCodesToExceptions(true);
    return provider;
}

@Bean
public KerberosAuthenticationProvider kerberosAuthenticationProvider() {
    KerberosAuthenticationProvider provider = new KerberosAuthenticationProvider();
    SunJaasKerberosClient client = new SunJaasKerberosClient();
    client.setDebug(true);
    provider.setKerberosClient(client);
    provider.setUserDetailsService(kerberosUserService());
    return provider;
}

@Bean
public KerberosServiceAuthenticationProvider kerberosServiceAuthenticationProvider() {
    KerberosServiceAuthenticationProvider provider = new KerberosServiceAuthenticationProvider();
    provider.setTicketValidator(sunJaasKerberosTicketValidator());
    provider.setUserDetailsService(kerberosUserService());
    return provider;
}

@Bean
public SunJaasKerberosTicketValidator sunJaasKerberosTicketValidator() {
    SunJaasKerberosTicketValidator ticketValidator = new SunJaasKerberosTicketValidator();
    ticketValidator.setServicePrincipal(kerberosPrincipal);
    File f = new File(keytabFile);
    try {
        LOG.info(String.format("Absolute: %s, Canonical: %s", f.getAbsolutePath(), f.getCanonicalPath()));
        if(f.exists()){
            LOG.info("File exists.");
        }
        else{
            LOG.info("File DOES NOT exist.");
        }
    } catch (IOException e) {
        e.printStackTrace();
    }
    ticketValidator.setKeyTabLocation(new FileSystemResource(f));
    ticketValidator.setDebug(true);
    return ticketValidator;
}

@Bean
public SpnegoAuthenticationProcessingFilter spnegoAuthenticationProcessingFilter(AuthenticationManager authenticationManager) {
    SpnegoAuthenticationProcessingFilter filter = new SpnegoAuthenticationProcessingFilter();
    filter.setAuthenticationManager(authenticationManager);
    return filter;
}

@Bean
public KerberosUserDetailsService kerberosUserService() {
    return new KerberosUserDetailsService();
}

Anyway to get this to work for both? I was thinking about making a custom authentication provider that would handle the requests, but wasn't sure if that would work.

Innova
  • 1,751
  • 2
  • 18
  • 26
  • What exactly do you want to do? Have some endpoints use kerberos and others your custom AD authentication OR have both available on all endpoints? What error did you experience when using both together, e.g. `auth.authenticationProvider(kerberosAuthenticationProvider()).authenticationProvider(kerberosServiceAuthenticationProvider()).authenticationProvider(customAuthenticationProvider());` – Gunnar Kiesel Jan 29 '16 at 13:44

3 Answers3

0

Easiest think to do is have to SpringDistpatcherServlets in your Web.xml based on url mapping. Each url mapping is then in a different spring context. Each spring context can then have its own security.

Essex Boy
  • 7,565
  • 2
  • 21
  • 24
0
 public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
    auth.authenticationProvider(kerberosAuthenticationProvider());
auth.authenticationProvider(kerberosServiceAuthenticationProvider());
    auth.authenticationProvider(customAuthenticationProvider());
}
  • 1
    Welcome to Stack Overflow. Please review [How do I write a good answer](https://meta.stackexchange.com/questions/7656/how-do-i-write-a-good-answer-to-a-question). Adding a brief explanation would have been good. – Rick Nov 24 '17 at 12:02
0

What you are doing is adding both authenticationProviders for Kerberos and your custom one to the AuthenticationManagerBuilder. This should register all of them.

What happens during runtime:

There is a ProviderManager handling all of your registered AuthenticationProvider and the build in one.

  • First it tries to authenticate as anonymous user. If the requested URL is set to permitAll this is the end of the story
  • Then the ProviderManager iterates over all of your AuthenticationProvider in the order you provided them. It checks if they support the authentication and tries to authenticate with them. If it fails it moves to the next one (saving the exception if there was one)
  • Finally there is a DaoAuthenticationProvider handling normal username-password-credentials
  • If any provider succeeds, the user is logged in, otherwise the saved exception is thrown.

Conclusion: What you did should be quite close if not exactly what you want. For your Kerberos protected endpoint it would use the kerberos AuthenticationProvider. For the other endpoints it would try and fail Kerberos and move on to your custom provider.

If something is still not working I would recommend setting a breakpoint within the class org.springframework.security.authentication.ProviderManager and see how it is handling your provider.

Ruik
  • 1,222
  • 1
  • 20
  • 36