X.509 Authentication in Spring Security
1. обзор
В этой статье мы сосредоточимся на основных случаях использованияX.509 certificate authentication -verifying the identity of a communication peer при использовании протоколаHTTPS (HTTP over SSL).
Проще говоря - пока установлено безопасное соединение, клиент проверяет сервер в соответствии с его сертификатом (выданным доверенным центром сертификации).
Но помимо этогоX.509 вSpring Security можно использовать дляverify the identity of a client через сервер при подключении. Это называется“mutual authentication, “, и здесь мы также рассмотрим, как это делается.
Наконец, коснемсяwhen it makes sense to use this kind of authentication.
Чтобы продемонстрировать проверку сервера, мы создадим простое веб-приложение и установим специальный центр сертификации в браузере.
И дляmutual authentication мы создадим сертификат клиента и модифицируем наш сервер, чтобы разрешить доступ только проверенным клиентам.
2. Хранилища ключей
Optional Requirement: чтобы использовать криптостойкие ключи вместе с функциями шифрования и дешифрования, вам необходимо установить‘Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy Files' в вашемJVM.
Их можно загрузить, например, сOracle (следуйте инструкциям по установке, включенным в загрузку). Некоторые дистрибутивы Linux также предоставляют устанавливаемый пакет через своих менеджеров пакетов.
Чтобы реализоватьX.509 authentication в приложении Spring, мы используемfirst create a keystore in the Java Key-Store (JKS) format.
Этотkeystore должен содержатьcontain a valid certificate of authority or a chain of certificate authorities и собственный сертификат для нашего сервера. Последний должен быть подписан одним из включенныхauthorities и должен быть назван в честьhostname, на котором работает сервер; мы будем использовать здесь приложениеJavakeytool.
To simplify процесс созданияkeys иcertificates с использованиемkeytool, кодаon Github, предоставляет прокомментированныйMakefile для GNUmake , содержащий все шаги, необходимые для завершения этого раздела. Вы также можете легко настроить его с помощью нескольких переменных среды.
Tip: Какall-in-one step, вы можете запуститьmake без аргументов. Это создастkeystore, atruststore и два сертификата для импорта в ваш браузер (один дляlocalhost и один для пользователя с именем“cid”).
Для создания новогоkeystore с центром сертификации мы можем запуститьmake следующим образом:
$> make create-keystore PASSWORD=changeit
Теперь мы добавим сертификат для нашего хоста разработки к созданномуkeystore и подпишем его нашимcertificate authority:
$> make add-host HOSTNAME=localhost
Чтобы разрешитьclient authentication, нам также понадобитсяkeystore с именем“truststore”. Этотtruststore должен содержать действительные сертификаты нашегоcertificate authority и всех разрешенных клиентов. Для справки по использованиюkeytool, пожалуйста, посмотритеMakefile в следующих разделах:
$> make create-truststore PASSWORD=changeit
$> make add-client CLIENTNAME=cid
3. Пример приложения
Наш проект защищенного сервера SSL будет состоять из аннотированного класса приложения@SpringBootApplication (который является разновидностью@Configuration), файла конфигурацииapplication.properties и очень простого внешнего интерфейса в стиле MVC.
Все, что нужно сделать приложению, - это представить страницуHTML с сообщением“Hello {User}!”. Таким образом, мы можем проверить сертификат сервера в браузере, чтобы убедиться, что соединение проверено и защищено.
Сначала мы создаем новый проектMaven с тремя включенными пакетамиSpring Boot Starter:
org.springframework.boot
spring-boot-starter-security
1.4.0.RELEASE
org.springframework.boot
spring-boot-starter-web
1.4.0.RELEASE
org.springframework.boot
spring-boot-starter-thymeleaf
1.4.0.RELEASE
В качестве следующего шага мы создадим основной класс приложения и пользовательский контроллер:
@SpringBootApplication
public class X509AuthenticationServer {
public static void main(String[] args) {
SpringApplication.run(X509AuthenticationServer.class, args);
}
}
@Controller
public class UserController {
@RequestMapping(value = "/user")
public String user(Model model, Principal principal) {
UserDetails currentUser
= (UserDetails) ((Authentication) principal).getPrincipal();
model.addAttribute("username", currentUser.getUsername());
return "user";
}
}
Теперь мы сообщаем приложению, где они могут найти нашkeystore и как к нему можно получить доступ. Мы устанавливаемSSL в состояние «включено» и меняем стандартный порт прослушивания наindicate a secured connection.
Кроме того, мы настраиваемuser-details для доступа к нашему серверу черезBasic Authentication:
server.ssl.key-store=../keystore/keystore.jks
server.ssl.key-store-password=${PASSWORD}
server.ssl.key-alias=localhost
server.ssl.key-password=${PASSWORD}
server.ssl.enabled=true
server.port=8443
security.user.name=Admin
security.user.password=admin
Это будет HTML-шаблон, расположенный в папкеresources/templates:
X.509 Authentication Demo
Hello !
Прежде чем мы закончим этот раздел и посмотрим на сайт, нам все равно нужно установить сгенерированный центр сертификации какtrusted certificate в браузере по нашему выбору.
Пример установки нашегоcertificate authority дляMozilla Firefox будет выглядеть следующим образом:
-
Введитеabout:preferences в адресной строке
-
ОткрытьAdvanced → Certificates → View Certificates → Authorities
-
Нажмите наImport
-
Найдите папкуexample tutorials и ее подпапкуspring-security-x509/keystore
-
Выберите файлca.crt и щелкнитеOK
-
Выберите «Trust this CA to identify websites” и щелкнитеOK»
Note: Если вы не хотите добавлять нашcertificate authority в списокtrusted authorities, позже у вас будет возможность сделатьexception и показать сайт жестким, даже когда он упоминается как небезопасный. Но тогда вы увидите «желтый восклицательный знак» в адресной строке, указывающий на небезопасное соединение!
После этого мы перейдем к модулю spring-security-x509-basic-auth и запустим:
mvn spring-boot:run
Наконец, мы нажимаемhttps://localhost:8443/user, вводим наши учетные данные пользователя изapplication.properties и должны увидеть сообщение“Hello Admin!”. Теперь мы можем проверить статус подключения, щелкнув значок «зеленый замок» в адресной строке, и это должно быть защищенное соединение.
4. Взаимная аутентификация
В этом разделе мы используемSpring Security, чтобы предоставить пользователям доступ к нашему демонстрационному сайту. Процедура делает форму входа устаревшей.
Но прежде чем мы продолжим модифицировать наш сервер, мы кратко обсудим, когда имеет смысл предоставлять такой вид аутентификации.
Плюсы:
-
Закрытый ключX.509 client certificate -stronger than any user-defined password. Но это нужно держать в секрете!
-
С сертификатомidentity клиентаis well-known легко проверить.
-
Нет больше забытых паролей!
Минусы:
-
Вы должны помнить, что для каждого пользователя, который должен быть проверен сервером, его сертификат должен быть установлен в настроенномtruststore. Для небольших приложений с небольшим количеством клиентов это возможно,with an increasing number of clients it may lead to complex key-management for users.
-
Закрытый ключ сертификата должен быть установлен в клиентском приложении. Фактически:X.509 client authenticationis device dependent, что делает невозможным использование такого типа аутентификации в общественных местах, например в интернет-кафе.
-
Должен быть механизм для отзыва скомпрометированных клиентских сертификатов.
Чтобы продолжить, мы изменяем нашX509AuthenticationServer, чтобы он продолжался отWebSecurityConfigurerAdapter и переопределял один из предоставленных методов настройки. Здесь мы настраиваем механизмx.509 для анализа поляCommon Name (CN) сертификата для извлечения имен пользователей.
С этими извлеченными именами пользователейSpring Security ищет в предоставленномUserDetailsService подходящих пользователей. Поэтому мы также реализуем этот интерфейс сервиса, содержащий одного демо-пользователя.
Tip: В производственной среде этотUserDetailsService может загружать своих пользователей, например, по ссылке: / spring-jdbc-jdbctemplate.
Вы должны заметить, что мы аннотируем наш класс с помощью@EnableWebSecurity и@EnableGlobalMethodSecurity с включенной предварительной / пост-авторизацией.
С последним мы можем аннотировать наши ресурсы с помощью@PreAuthorize и@PostAuthorize для детального контроля доступа:
@SpringBootApplication
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class X509AuthenticationServer extends WebSecurityConfigurerAdapter {
...
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest().authenticated()
.and()
.x509()
.subjectPrincipalRegex("CN=(.*?)(?:,|$)")
.userDetailsService(userDetailsService());
}
@Bean
public UserDetailsService userDetailsService() {
return new UserDetailsService() {
@Override
public UserDetails loadUserByUsername(String username) {
if (username.equals("cid")) {
return new User(username, "",
AuthorityUtils
.commaSeparatedStringToAuthorityList("ROLE_USER"));
}
}
};
}
}
Как было сказано ранее, теперь мы можем использоватьExpression-Based Access Control в нашем контроллере. В частности, наши аннотации авторизации соблюдаются из-за аннотации@EnableGlobalMethodSecurity в нашем@Configuration: __
@Controller
public class UserController {
@PreAuthorize("hasAuthority('ROLE_USER')")
@RequestMapping(value = "/user")
public String user(Model model, Principal principal) {
...
}
}
Обзор всех возможных вариантов авторизации можно найти вofficial documentation.
В качестве последнего шага модификации мы должны сообщить приложению, где находится нашtruststore и чтоSSL client authentication необходимо (server.ssl.client-auth=need).
Итак, мы помещаем в нашapplication.properties следующее:
server.ssl.trust-store=../keystore/truststore.jks
server.ssl.trust-store-password=${PASSWORD}
server.ssl.client-auth=need
Теперь, если мы запустим приложение и укажем в браузереhttps://localhost:8443/user, мы узнаем, что узел не может быть проверен, и он отказывает в открытии нашего веб-сайта. Таким образом, мы также должны установить нашclient certificate, который показан здесь в качестве примера дляMozilla Firefox:
-
Введитеabout:preferences в адресной строке
-
ОткрытьAdvanced → View Certificates → Your Certificates
-
Нажмите наImport
-
Найдите папкуexample tutorials и ее подпапкуspring-security-x509/keystore
-
Выберите файлcid.p12 и щелкнитеOK
-
Введите пароль для сертификата и нажмитеOK
В качестве последнего шага мы обновляем нашу вкладку браузера, содержащую веб-сайт, и выбираем наш сертификат клиента во вновь открывшемся диалоговом окне выбора.
Если мы видим приветственное сообщение типа“Hello cid!”, значит, все прошло успешно!
5. Взаимная аутентификация с помощью XML
Также возможно добавлениеX.509 client authentication кhttp security configuration in XML:
...
...
Чтобы настроить базовыйTomcat, мы должны поместить нашиkeystore и нашиtruststore в его папкуconf и отредактироватьserver.xml:
Tip: Если дляclientAuth установлено значение“want”,SSL по-прежнему включен, даже если клиент не предоставляет действительный сертификат. Но в этом случае мы должны использовать второй механизм аутентификации, например форму входа, для доступа к защищенным ресурсам.
6. Заключение
In summary, мы узнали, как создатьkeystore, содержащийcertificate authority иself-signed certificate for our development environment.
Мы создалиtruststore containing a certificate authority and a client certificate, и мы использовали оба параметра дляverify our server на стороне клиентаand our client на стороне сервера.
Если вы изучилиMakefile, вы сможете использоватьcreate certificates, make certificate-requests and import signed certificates, используя Javakeytool.
Кроме того, теперь вы должны иметь возможностьexport a client certificate into the PKCS12 format и использовать его в клиентском приложении, таком как браузер, например,Mozilla Firefox.
И мы обсудили, когда имеет смысл использоватьSpring Security X.509 client authentication, поэтому вам решать, внедрять его в свое веб-приложение или нет.
И в заключение вы найдете исходный код этой статьиon Github.