1. Обзор
В этом руководстве мы представим обзор Spring Security Kerberos.
Мы напишем клиент Kerberos на Java, который авторизует себя для доступа к нашей службе Kerberized. И мы запустим собственный встроенный центр распространения ключей для выполнения полной сквозной проверки подлинности Kerberos. Все это без какой-либо внешней инфраструктуры благодаря Spring Security Kerberos .
2. Kerberos и его преимущества
Kerberos — это сетевой протокол аутентификации, созданный Массачусетским технологическим институтом в 1980-х годах и особенно полезный для централизации аутентификации в сети.
В 1987 году Массачусетский технологический институт выпустил его для сообщества открытого исходного кода , и он все еще находится в стадии активной разработки. В 2005 году он был канонизирован как стандарт IETF в соответствии с RFC 4120 . ** **
Обычно Kerberos используется в корпоративной среде . Там он защищает среду таким образом, что пользователю не нужно проходить аутентификацию для каждой службы отдельно . Это архитектурное решение известно как единый вход
. ``
Проще говоря, Kerberos — это система продажи билетов. Пользователь проходит аутентификацию один раз и получает Билет на предоставление Билета (TGT). Затем сетевая инфраструктура обменивает этот TGT на сервисные билеты. Эти служебные билеты позволяют пользователю взаимодействовать с инфраструктурными службами до тех пор, пока действует TGT, что обычно составляет пару часов.
Поэтому здорово, что пользователь подписывает только один раз. Но есть и преимущество в плане безопасности: в такой среде пароль пользователя никогда не передается по сети . Вместо этого Kerberos использует его как фактор для создания другого секретного ключа, который будет использоваться для шифрования и дешифрования сообщений.
Еще одним преимуществом является то, что мы можем управлять пользователями из центрального места, скажем, с помощью LDAP. Поэтому, если мы отключим учетную запись в нашей централизованной базе данных для данного пользователя, мы лишим его доступа в нашей инфраструктуре. Таким образом, администраторам не нужно отзывать доступ отдельно в каждом сервисе.
Введение в аутентификацию SPNEGO/Kerberos в Spring содержит подробный обзор технологии.
3. Керберизированная среда
Итак, давайте создадим среду для аутентификации по протоколу Kerberos. Среда будет состоять из трех отдельных приложений, которые будут работать одновременно.
Во- первых, у нас будет центр распространения ключей , который будет действовать как точка аутентификации. Далее мы напишем клиентское и сервисное приложения, которые настроим для использования протокола Kerberos.
Теперь для запуска Kerberos требуется небольшая установка и настройка. Однако мы будем использовать Spring Security Kerberos , поэтому запустим Центр распространения ключей программно во встроенном режиме. Кроме того, MiniKdc
, показанный ниже, полезен в случае интеграционного тестирования с инфраструктурой Kerberized.
3.1. Запуск центра распространения ключей
Во-первых, мы запустим наш Центр распространения ключей, который будет выдавать нам TGT:
String[] config = MiniKdcConfigBuilder.builder()
.workDir(prepareWorkDir())
.principals("client/localhost", "HTTP/localhost")
.confDir("minikdc-krb5.conf")
.keytabName("example.keytab")
.build();
MiniKdc.main(config);
По сути, мы предоставили MiniKdc
набор принципалов и файл конфигурации; кроме того, мы указали MiniKdc
, как следует называть созданную
им таблицу ключей .
MiniKdc
сгенерирует файл krb5.conf
, который мы предоставим нашим клиентским и сервисным приложениям. Этот файл содержит информацию о том, где найти наш KDC — хост и порт для данной области.
MiniKdc.main
запускает KDC и должен вывести что-то вроде:
Standalone MiniKdc Running
---------------------------------------------------
Realm : EXAMPLE.COM
Running at : localhost:localhost
krb5conf : .\spring-security-sso\spring-security-sso-kerberos\krb-test-workdir\krb5.conf
created keytab : .\spring-security-sso\spring-security-sso-kerberos\krb-test-workdir\example.keytab
with principals : [client/localhost, HTTP/localhost]
3.2. Клиентское приложение
Нашим клиентом будет приложение Spring Boot, использующее RestTemplate
для выполнения вызовов внешнего API REST.
Но вместо этого мы собираемся использовать KerberosRestTemplate
. Ему понадобится keytab и принципал клиента:
@Configuration
public class KerberosConfig {
@Value("${app.user-principal:client/localhost}")
private String principal;
@Value("${app.keytab-location}")
private String keytabLocation;
@Bean
public RestTemplate restTemplate() {
return new KerberosRestTemplate(keytabLocation, principal);
}
}
Вот и все! KerberosRestTemplate
согласовывает для нас клиентскую часть протокола Kerberos.
Итак, давайте создадим быстрый класс, который будет запрашивать некоторые данные из службы Kerberized, размещенной в конечной точке app.access-url
:
@Service
class SampleClient {
@Value("${app.access-url}")
private String endpoint;
private RestTemplate restTemplate;
// constructor, getter, setter
String getData() {
return restTemplate.getForObject(endpoint, String.class);
}
}
Итак, давайте сейчас создадим наше сервисное приложение, чтобы этому классу было что вызывать!
3.3. Сервисное приложение
Мы будем использовать Spring Security, настроив его с помощью соответствующих bean-компонентов, специфичных для Kerberos.
Также обратите внимание, что служба будет иметь своего принципала и также будет использовать keytab:
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Value("${app.service-principal:HTTP/localhost}")
private String servicePrincipal;
@Value("${app.keytab-location}")
private String keytabLocation;
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/", "/home").permitAll()
.anyRequest().authenticated()
.and()
.exceptionHandling()
.authenticationEntryPoint(spnegoEntryPoint())
.and()
.formLogin()
.loginPage("/login").permitAll()
.and()
.logout().permitAll()
.and()
.addFilterBefore(spnegoAuthenticationProcessingFilter(authenticationManagerBean()),
BasicAuthenticationFilter.class);
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.authenticationProvider(kerberosAuthenticationProvider())
.authenticationProvider(kerberosServiceAuthenticationProvider());
}
@Bean
public KerberosAuthenticationProvider kerberosAuthenticationProvider() {
KerberosAuthenticationProvider provider = new KerberosAuthenticationProvider();
// provider configuration
return provider;
}
@Bean
public SpnegoEntryPoint spnegoEntryPoint() {
return new SpnegoEntryPoint("/login");
}
@Bean
public SpnegoAuthenticationProcessingFilter spnegoAuthenticationProcessingFilter(
AuthenticationManager authenticationManager) {
SpnegoAuthenticationProcessingFilter filter = new SpnegoAuthenticationProcessingFilter();
// filter configuration
return filter;
}
@Bean
public KerberosServiceAuthenticationProvider kerberosServiceAuthenticationProvider() {
KerberosServiceAuthenticationProvider provider = new KerberosServiceAuthenticationProvider();
// auth provider configuration
return provider;
}
@Bean
public SunJaasKerberosTicketValidator sunJaasKerberosTicketValidator() {
SunJaasKerberosTicketValidator ticketValidator = new SunJaasKerberosTicketValidator();
// validator configuration
return ticketValidator;
}
}
Вступительная статья содержит всю описанную выше реализацию, поэтому для краткости мы опускаем здесь полные методы.
Обратите внимание, что мы настроили Spring Security для аутентификации SPNEGO . Таким образом, мы сможем проходить аутентификацию по протоколу HTTP, хотя мы также можем добиться аутентификации SPNEGO с помощью ядра Java .
4. Тестирование
Теперь мы запустим интеграционный тест, чтобы показать, что наш клиент успешно извлекает данные с внешнего сервера по протоколу Kerberos . Чтобы запустить этот тест, нам нужно, чтобы наша инфраструктура работала, поэтому MiniKdc
и наше сервисное приложение должны быть запущены.
По сути, мы будем использовать наш SampleClient
из клиентского приложения, чтобы сделать запрос к нашему сервисному приложению. Давайте проверим это:
@Autowired
private SampleClient sampleClient;
@Test
public void givenKerberizedRestTemplate_whenServiceCall_thenSuccess() {
assertEquals("data from kerberized server", sampleClient.getData());
}
Обратите внимание, что мы также можем доказать важность KerberizedRestTemplate
, запустив службу без него:
@Test
public void givenRestTemplate_whenServiceCall_thenFail() {
sampleClient.setRestTemplate(new RestTemplate());
assertThrows(RestClientException.class, sampleClient::getData);
}
В качестве примечания: есть шанс, что наш второй тест может повторно использовать билет, уже сохраненный в кэше учетных данных . Это может произойти из-за автоматического согласования SPNEGO, используемого в HttpUrlConnection
.
В результате данные могут действительно вернуться, что сделает наш тест недействительным. Таким образом, в зависимости от наших потребностей, мы можем отключить использование кэша билетов через системное свойство http.use.global.creds=false.
5. Вывод
В этом руководстве мы изучили Kerberos для централизованного управления пользователями и то, как Spring Security поддерживает протокол Kerberos и механизм аутентификации SPNEGO.
Мы использовали MiniKdc
для установки встроенного KDC, а также создали очень простой клиент и сервер с поддержкой Kerberos. Эта настройка была удобна для исследования и особенно удобна, когда мы создали интеграционный тест для проверки.
Теперь мы только что поцарапали поверхность. Чтобы углубиться, посетите вики-страницу Kerberos или RFC . Также будет полезна официальная страница документации . Помимо этого, чтобы увидеть, как это можно сделать в основной Java, в следующем руководстве Oracle это подробно показано.
Как обычно, код можно найти на нашей странице GitHub .