1. Обзор
Spring Cloud Config – это клиент-серверный подход Spring для хранения и обслуживания распределенных конфигураций в нескольких приложениях и средах.
Это хранилище конфигурации идеально версируется под управлением Git version control и может быть изменено во время выполнения приложения. Хотя он очень хорошо подходит для приложений Spring, использующих все поддерживаемые форматы файлов конфигурации вместе с конструкциями , такими как Environment , PropertySource или @Value , он может использоваться в любой среде, работающей на любом языке программирования.
В этой статье мы сосредоточимся на примере того, как настроить сервер конфигурации Git -backend, использовать его в простом REST сервере приложений и настроить безопасную среду, включая зашифрованные значения свойств.
2. Настройка проекта и зависимости
Чтобы подготовиться к написанию некоторого кода, мы сначала создаем два новых проекта Maven . Серверный проект опирается на модуль spring-cloud-config-server , а также на пакеты spring-boot-starter-security и spring-boot-starter-web starter:
org.springframework.cloud spring-cloud-config-server org.springframework.boot spring-boot-starter-security org.springframework.boot spring-boot-starter-web
Однако для клиентского проекта нам понадобятся только spring-cloud-starter-config и spring-boot-starter-web модули :
org.springframework.cloud spring-cloud-starter-config org.springframework.boot spring-boot-starter-web
3. Реализация Сервера Конфигурации
Основной частью приложения является класс конфигурации – более конкретно @SpringBootApplication – который извлекает всю необходимую настройку через автоконфигурацию аннотацию @EnableConfigServer:
@SpringBootApplication @EnableConfigServer public class ConfigServer { public static void main(String[] arguments) { SpringApplication.run(ConfigServer.class, arguments); } }
Теперь нам нужно настроить сервер порт , на котором прослушивается наш сервер, и Git -url, который предоставляет содержимое конфигурации с контролем версий. Последний может использоваться с такими протоколами, как http , ssh или простой файл в локальной файловой системе.
Совет: Если вы планируете использовать несколько экземпляров сервера конфигурации, указывающих на один и тот же репозиторий конфигурации, вы можете настроить сервер для клонирования репозитория в локальную временную папку. Но имейте в виду, что частные репозитории с двухфакторной аутентификацией трудно обрабатывать! В таком случае проще клонировать их в локальной файловой системе и работать с копией.
Существуют также некоторые переменные-заполнители и шаблоны поиска для настройки URL-адреса репозитория ; но это выходит за рамки нашей статьи. Если вы заинтересованы, официальная документация-хорошее место для начала.
Нам также необходимо установить имя пользователя и пароль для Basic-Authentication в нашем application.properties , чтобы избежать автоматического создания пароля при каждом перезапуске приложения:
server.port=8888 spring.cloud.config.server.git.uri=ssh://localhost/config-repo spring.cloud.config.server.git.clone-on-start=true spring.security.user.name=root spring.security.user.password=s3cr3t
4. Репозиторий Git в качестве хранилища конфигурации
Чтобы завершить работу нашего сервера, мы должны инициализировать репозиторий Git под настроенным URL-адресом, создать несколько новых файлов свойств и популяризировать их с некоторыми значениями.
Имя файла конфигурации составляется как обычная пружина application.properties , но вместо слова “приложение” используется сконфигурированное имя, например значение свойства ‘spring.application.name используется’ клиента, за которым следует тире и активный профиль. Например:
$> git init $> echo 'user.role=Developer' > config-client-development.properties $> echo 'user.role=User' > config-client-production.properties $> git add . $> git commit -m 'Initial config-client properties'
Устранение неполадок: Если вы столкнулись с проблемами аутентификации, связанными с ssh , дважды проверьте ~/.ssh/known_hosts и ~/. ssh/authorized_keys на вашем ssh-сервере!
5. Запрос конфигурации
Теперь мы можем запустить наш сервер. API конфигурации Git -backend, предоставляемый нашим сервером, можно запросить по следующим путям:
/{application}/{profile}[/{label}] /{application}-{profile}.yml /{label}/{application}-{profile}.yml /{application}-{profile}.properties /{label}/{application}-{profile}.properties
В котором заполнитель {label} ссылается на ветвь Git, {application} на имя приложения клиента и {profile} на текущий активный профиль приложения клиента.
Таким образом, мы можем получить конфигурацию для нашего запланированного клиента конфигурации, работающего под профилем разработки в ветке master через:
$> curl http://root:[email protected]:8888/config-client/development/master
6. Реализация Клиента
Далее, давайте позаботимся о клиенте. Это будет очень простое клиентское приложение, состоящее из контроллера REST с одним методом GET .
Конфигурация, чтобы получить наш сервер, должна быть помещена в файл ресурсов с именем bootstrap.application , потому что этот файл (как следует из названия) будет загружен очень рано при запуске приложения:
@SpringBootApplication @RestController public class ConfigClient { @Value("${user.role}") private String role; public static void main(String[] args) { SpringApplication.run(ConfigClient.class, args); } @GetMapping( value = "/whoami/{username}", produces = MediaType.TEXT_PLAIN_VALUE) public String whoami(@PathVariable("username") String username) { return String.format("Hello! You're %s and you'll become a(n) %s...\n", username, role); } }
В дополнение к имени приложения мы также помещаем активный профиль и сведения о соединении в наш bootstrap.properties :
spring.application.name=config-client spring.profiles.active=development spring.cloud.config.uri=http://localhost:8888 spring.cloud.config.username=root spring.cloud.config.password=s3cr3t
Чтобы проверить, правильно ли получена конфигурация с нашего сервера и значение роли вводится в наш метод контроллера, мы просто сворачиваем его после загрузки клиента:
$> curl http://localhost:8080/whoami/Mr_Pink
Если ответ будет следующим, наш Spring Cloud Config Server и его клиент пока работают нормально:
Hello! You're Mr_Pink and you'll become a(n) Developer...
7. Шифрование и дешифрование
Требование : Для использования криптографически надежных ключей вместе с функциями шифрования и дешифрования Spring вам необходимо “Файлы политики юрисдикции неограниченной силы Java Cryptography Extension (JCE)” , установленные в вашей JVM. Их можно загрузить, например, из Oracle . Для установки следуйте инструкциям, включенным в загрузку. Некоторые дистрибутивы Linux также предоставляют устанавливаемый пакет через свои менеджеры пакетов.
Поскольку сервер конфигурации поддерживает шифрование и расшифровку значений свойств, вы можете использовать общедоступные хранилища в качестве хранилища конфиденциальных данных, таких как имена пользователей и пароли. Зашифрованные значения имеют префикс строки {cipher} и могут быть сгенерированы вызовом REST пути ‘/encrypt’ , если сервер настроен на использование симметричного ключа или пары ключей.
Также доступна конечная точка для расшифровки. Обе конечные точки принимают путь, содержащий заполнители для имени приложения и его текущего профиля: ‘/*/{name}/{profile}’ . Это особенно полезно для управления криптографией для каждого клиента. Однако, прежде чем они станут полезными, вы должны настроить криптографический ключ, что мы сделаем в следующем разделе.
Совет: Если вы используете curl для вызова API en-/decryption, лучше использовать параметр –data-urlencode (вместо –data/-d ) или явно установить заголовок “Content-Type” в “text/plain” . Это обеспечивает правильную обработку специальных символов, таких как ” + ” в зашифрованных значениях.
Если значение не может быть расшифровано автоматически при извлечении через клиент, его ключ переименовывается с самим именем, с префиксом “недопустимый”. Это должно предотвратить, например, использование зашифрованного значения в качестве пароля.
Совет: При настройке репозитория, содержащего файлы YAML, вы должны окружить зашифрованные и префиксные значения одинарными кавычками! Со свойствами это не так.
7.1. CSRF
По умолчанию Spring Security включает защиту CSRF для всех запросов, отправленных в наше приложение.
Поэтому, чтобы иметь возможность использовать конечные точки /encrypt и /decrypt , давайте отключим для них CSRF:
@Configuration public class SecurityConfiguration extends WebSecurityConfigurerAdapter { @Override public void configure(HttpSecurity http) throws Exception { http.csrf() .ignoringAntMatchers("/encrypt/**") .ignoringAntMatchers("/decrypt/**"); super.configure(http); } }
7.2. Управление Ключами
Сервер конфигурации по умолчанию включен для шифрования значений свойств симметричным или асимметричным способом.
Чтобы использовать симметричную криптографию , вам просто нужно установить свойство ‘encrypt.key’ в вашем application.properties в секрет по вашему выбору . В качестве альтернативы вы можете передать переменную окружения ENCRYPT_KEY .
Для асимметричной криптографии вы можете установить ‘encrypt.key’ в PEM -кодированное строковое значение или настроить хранилище ключей для использования.
Поскольку нам нужна высокозащищенная среда для нашего демо-сервера, мы выбрали последний вариант и создали новое хранилище ключей, включая пару ключей RSA , с помощью Java keytool first:
$> keytool -genkeypair -alias config-server-key \ -keyalg RSA -keysize 4096 -sigalg SHA512withRSA \ -dname 'CN=Config Server,OU=Spring Cloud,O=Baeldung' \ -keypass my-k34-s3cr3t -keystore config-server.jks \ -storepass my-s70r3-s3cr3t
После этого мы добавляем созданное хранилище ключей в файл bootstrap.properties нашего сервера и повторно запускаем его:
encrypt.keyStore.location=classpath:/config-server.jks encrypt.keyStore.password=my-s70r3-s3cr3t encrypt.keyStore.alias=config-server-key encrypt.keyStore.secret=my-k34-s3cr3t
В качестве следующего шага мы можем запросить конечную точку шифрования и добавить ответ в качестве значения в конфигурацию в нашем репозитории:
$> export PASSWORD=$(curl -X POST --data-urlencode d3v3L \ http://root:[email protected]:8888/encrypt) $> echo "user.password={cipher}$PASSWORD" >> config-client-development.properties $> git commit -am 'Added encrypted password' $> curl -X POST http://root:[email protected]:8888/refresh
Чтобы проверить, правильно ли работает наша настройка, мы изменяем класс Config Client и перезапускаем ваш клиент:
@SpringBootApplication @RestController public class ConfigClient { ... @Value("${user.password}") private String password; ... public String whoami(@PathVariable("username") String username) { return String.format("Hello! You're %s and you'll become a(n) %s, " + "but only if your password is '%s'!\n", username, role, password); } }
Окончательный запрос к нашему клиенту покажет нам, правильно ли расшифровано значение нашей конфигурации:
$> curl http://localhost:8080/whoami/Mr_Pink Hello! You're Mr_Pink and you'll become a(n) Developer, \ but only if your password is 'd3v3L'!
7.3. Использование Нескольких Ключей
Если вы хотите использовать несколько ключей для шифрования и дешифрования, например: выделенный для каждого обслуживаемого приложения, вы можете добавить еще один префикс в виде {name:value} между префиксом {cipher} и значением свойства BASE64 -encoded.
Сервер конфигурации понимает префиксы, такие как {secret:my-crypto-secret} или {key:my-key-alias} почти из коробки. Для последнего варианта требуется настроенное хранилище ключей в вашем application.properties . В этом хранилище ключей выполняется поиск соответствующего псевдонима ключа. Например:
user.password={cipher}{secret:my-499-s3cr3t}AgAMirj1DkQC0WjRv... user.password={cipher}{key:config-client-key}AgAMirj1DkQC0WjRv...
Для сценариев без хранилища ключей необходимо реализовать @Bean типа Локатор текстового шифратора , который обрабатывает поиск и возвращает TextEncryptor -Объект для каждого ключа.
7.4. Обслуживание Зашифрованных свойств
Если вы хотите отключить криптографию на стороне сервера и обрабатывать описание значений свойств локально, вы можете поместить следующее в application.properties вашего сервера:
spring.cloud.config.server.encrypt.enabled=false
Кроме того, вы можете удалить все остальные свойства ” encrypt.*”, чтобы отключить конечные точки REST .
8. Заключение
Теперь мы можем создать сервер конфигурации для предоставления набора файлов конфигурации из репозитория Git клиентским приложениям. Есть еще несколько вещей, которые вы можете сделать с таким сервером.
Например:
- Подавайте конфигурацию в формате YAML или Properties вместо JSON – также с разрешенными заполнителями. Что может быть полезно при его использовании в средах, отличных от Spring, где конфигурация напрямую не сопоставляется с PropertySource .
- Подавайте файлы конфигурации с открытым текстом – в свою очередь, необязательно с разрешенными заполнителями. Это может быть полезно, например, для обеспечения зависящей от среды конфигурации ведения журнала.
- Внедрите сервер конфигурации в приложение, где он настраивается из репозитория Git , вместо того, чтобы работать как автономное приложение, обслуживающее клиентов. Поэтому необходимо установить некоторые свойства начальной загрузки и/или удалить аннотацию @EnableConfigServer , что зависит от варианта использования.
- Сделайте сервер конфигурации доступным в Spring Netflix Eureka service discovery и включите автоматическое обнаружение серверов в клиентах конфигурации. Это становится важным, если сервер не имеет фиксированного местоположения или перемещается в своем местоположении.
И чтобы завершить, вы найдете исходный код этой статьи на Github .