Можно ли использовать два разных AuthenticationProvider с WebSecurityConfigurerAdapter?

У меня есть приложение на основе SpringBoot, с несколькими конечными точками. Из-за разных клиентов, которые будут получать доступ к конечным точкам, я бы хотел, чтобы их защищали разные провайдеры аутентификации. Некоторые конечные точки будут защищены Kerberos (KerberosServiceAuthenticationProvider - http://docs.spring.io/autorepo/docs/spring-security-kerberos/1.0.0.RC1/reference/htmlsingle/). Некоторые конечные точки будут защищены AD/LDAP (ActiveDirectoryLdapAuthenticationProvider).

В настоящее время он работает с Kerberos ИЛИ LDAP, но не с обоими:

@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();
}

В любом случае, чтобы это сработало для обоих? Я думал о создании собственного провайдера аутентификации, который бы обрабатывал запросы, но не был уверен, сработает ли это.

2 ответа

 public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
    auth.authenticationProvider(kerberosAuthenticationProvider());
auth.authenticationProvider(kerberosServiceAuthenticationProvider());
    auth.authenticationProvider(customAuthenticationProvider());
}

Вы добавляете оба authenticationProviders для Kerberos и ваш собственный для AuthenticationManagerBuilder. Это должно зарегистрировать их всех.

Что происходит во время выполнения:

E сть ProviderManager обработка всех ваших зарегистрированных AuthenticationProvider и сборка в одном.

  • Сначала он пытается пройти аутентификацию как анонимный пользователь. Если запрошенный URL установлен наpermitAll это конец истории
  • Тогда ProviderManager перебирает все ваши AuthenticationProviderв том порядке, в котором вы их предоставили. Он проверяет, поддерживают ли они аутентификацию, и пытается аутентифицироваться с ними. В случае неудачи он переходит к следующему (сохранение исключения, если оно было).
  • Наконец, есть DaoAuthenticationProvider обработка обычных учетных данных имени пользователя и пароля
  • Если какой-либо поставщик завершается успешно, пользователь входит в систему, в противном случае создается сохраненное исключение.

Вывод: то, что вы сделали, должно быть довольно близко, если не совсем то, что вы хотите. Для вашей защищенной Kerberos конечной точки будет использоваться KerberosAuthenticationProvider. Для других конечных точек он будет пытаться выйти из строя Kerberos и перейти к вашему настраиваемому провайдеру.

Если что-то все еще не работает, я бы рекомендовал установить точку останова в классе org.springframework.security.authentication.ProviderManager и посмотрите, как с этим справляется ваш провайдер.

Проще всего подумать о SpringDistpatcherServlets в вашем Web.xml, основанном на отображении URL. Каждое отображение URL тогда находится в другом весеннем контексте. Каждый весенний контекст может иметь свою собственную безопасность.

Другие вопросы по тегам