Можно ли использовать два разных 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 тогда находится в другом весеннем контексте. Каждый весенний контекст может иметь свою собственную безопасность.