1. Обзор
-
Spring Cloud Config ** - это клиент-серверный подход Spring для хранения и обслуживания распределенных конфигураций в различных приложениях и средах.
Это хранилище конфигурации идеально версионировано под управлением версией Git и может быть изменено во время выполнения приложения. Хотя он очень хорошо подходит для приложений Spring, использующих все поддерживаемые форматы файлов конфигурации вместе с такими конструкциями, как Environment , PropertySource или @Value , он может использоваться в любой среде, в которой работает любой язык программирования.
В этой статье мы рассмотрим пример настройки сервера конфигурации с поддержкой Git , его использования на простом сервере приложений REST и настройки безопасной среды, включая зашифрованные значения свойств.
2. Настройка проекта и зависимости
Чтобы подготовиться к написанию кода, мы сначала создадим два новых проекта Maven . Проект сервера основан на https://search.maven.org/classic/#search%7Cgav%7C1%7Cg%3A%22org.springframework.cloud%22%20AND%20a%3A%22spring-cloud-config- модуль сервера% 22[spring-cloud-config-server] , а также https://search.maven.org/classic/#search%7Cgav%7C1%7Cg%3A%22org.springframework.boot%22% 20AND% 20a% 3A% 22spring-boot-starter-security% 22[spring-boot-starter-security] и https://search.maven.org/classic/#search%7Cgav%7C1%7Cg%3A%22org .springframework.boot% 22% 20AND% 20a% 3A% 22spring-boot-starter-web% 22[spring-boot-starter-web] начальные пакеты:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
<version>1.1.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
<version>1.4.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>1.4.0.RELEASE</version>
</dependency>
Однако для клиентского проекта нам понадобится только https://search.maven.org/classic/#search%7Cgav%7C1%7Cg%3A%22org.springframework.cloud%22%20AND%20a%3A% 22spring-cloud-starter-config% 22[spring-cloud-starter-config] и https://search.maven.org/classic/#search%7Cgav%7C1%7Cg%3A%22org.springframework.boot% 22% 20AND% 20a% 3A% 22spring-boot-starter-web% 22[spring-boot-starter-web modules] :
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
<version>1.1.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>1.4.0.RELEASE</version>
</dependency>
3. Реализация сервера конфигурации
Основная часть приложения - это класс конфигурации, в частности ссылка:/spring-boot-application-configuration[ @ SpringBootApplication ], которая включает все необходимые настройки через auto-configure annotation @ EnableConfigServer:
@SpringBootApplication
@EnableConfigServer
public class ConfigServer {
public static void main(String[]arguments) {
SpringApplication.run(ConfigServer.class, arguments);
}
}
Теперь нам нужно настроить сервер port , на котором слушает наш сервер, и Git -url, который предоставляет содержимое конфигурации с управлением версиями. Последний может использоваться с такими протоколами, как http , ssh или простой file в локальной файловой системе.
-
Совет: ** Если вы планируете использовать несколько экземпляров сервера конфигурации, указывающих на один и тот же репозиторий конфигурации, вы можете настроить сервер для клонирования вашего хранилища во временную локальную папку. Но имейте в виду частные репозитории с двухфакторной аутентификацией, с ними трудно обращаться! В таком случае их проще клонировать в локальной файловой системе и работать с копией.
Для настройки repository-url также доступны некоторые переменные placeholder и шаблоны поиска ; но это выходит за рамки нашей статьи. Если вам интересно, официальная документация - хорошее место для начала.
Нам также нужно установить имя пользователя и пароль для 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
security.user.name=root
security.user.password=s3cr3t
4. Git-репозиторий как хранилище настроек
Чтобы завершить работу нашего сервера, мы должны инициализировать репозиторий Git под настроенным URL, создать несколько новых файлов свойств и популяризировать их с некоторыми значениями.
Имя файла конфигурации составлено как обычный Spring application.properties , но вместо слова «application» настроенное имя, например используется значение свойства ‘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 , предоставляемый нашим сервером, можно запросить по следующим путям:
----/{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);
}
@RequestMapping(
value = "/whoami/{username}",
method = RequestMethod.GET,
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);
}
}
В дополнение к имени приложения мы также помещаем активный профиль и сведения о соединении в наш b _ootstrap.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
Чтобы проверить, правильно ли получена конфигурация с нашего сервера и в методе контроллера введено role value , мы просто свернем ее после загрузки клиента:
$> 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' . Это обеспечивает правильную обработку специальных символов, таких как in ’, в зашифрованных значениях.
Если значение не может быть дешифровано автоматически при извлечении из клиента, его key переименовывается с самим именем, с префиксом слова «invalid» Это должно предотвратить, например, использование зашифрованного значения в качестве пароля.
-
Совет: ** При настройке репозитория, содержащего файлы YAML, вы должны заключать в зашифрованные и префиксные значения одинарные кавычки! Со свойствами это не так.
7.1. Ключевой менеджмент
По умолчанию сервер конфигурации включен для шифрования значений свойств симметричным или асимметричным способом.
-
Чтобы использовать симметричную криптографию ** , вам просто нужно установить свойство ‘encrypt.key’ в вашем application.properties на выбранный вами секрет __. В качестве альтернативы вы можете передать переменную среды ENCRYPT KEY .
-
Для асимметричной криптографии ** вы можете установить для ‘encrypt.key’ строковое значение в кодировке PEM или настроить использование keystore .
Поскольку для нашего демонстрационного сервера нам нужна среда с высокой степенью защиты, мы выбрали последний вариант и создали новое хранилище ключей, включая пару ключей 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
После этого мы добавляем созданное хранилище ключей в application.properties нашего сервера и перезапускаем его:
encrypt.key-store.location=classpath:/config-server.jks
encrypt.key-store.password=my-s70r3-s3cr3t
encrypt.key-store.alias=config-server-key
encrypt.key-store.secret=my-k34-s3cr3t
В качестве следующего шага мы можем запросить конечную точку шифрования и добавить ответ в качестве значения к конфигурации в нашем репозитории:
$> export PASSWORD=$(curl -X POST --data-urlencode d3v3L \
http://root:[email protected]:8888/encrypt)
$> echo "user.password=$PASSWORD" >> config-client-development.properties
$> git commit -am 'Added encrypted password'
$> curl -X POST http://root:[email protected]:8888/refresh
Чтобы проверить, правильно ли работает наша установка, мы модифицируем класс ConfigClient и перезапустим наш клиент:
@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.2. Использование нескольких ключей
Если вы хотите использовать несколько ключей для шифрования и дешифрования, например: выделенный для каждого обслуживаемого приложения, вы можете добавить другой префикс в форме \ {name: value} между префиксом \ {cipher} и BASE64 -закодированное значение свойства.
Конфигурационный сервер распознает префиксы, такие как \ {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 типа TextEncryptorLocator , который обрабатывает поиск и возвращает TextEncryptor -Object для каждого ключа.
7.3. Обслуживание зашифрованных свойств
Если вы хотите отключить криптографию на стороне сервера и локально обрабатывать дешифрование значений свойств, вы можете поместить следующее в application.properties вашего сервера:
spring.cloud.config.server.encrypt.enabled=false
Кроме того, вы можете удалить все другие свойства «encrypt. ** », Чтобы отключить конечные точки REST .
8. Заключение
Теперь мы можем создать сервер конфигурации для предоставления набора файлов конфигурации из репозитория Git клиентским приложениям.
Есть несколько других вещей, которые вы можете сделать с таким сервером.
Например:
-
Служите конфигурации в формате YAML или Properties вместо __JSON
– также с разрешенными заполнителями. Что может быть полезно при использовании его в средах, отличных от Spring, где конфигурация напрямую не сопоставлена с PropertySource__.
-
Служите простыми текстовыми файлами конфигурации - в свою очередь, опционально с
решенные заполнители. Это может быть полезно, например, для предоставления зависящей от среды конфигурации регистрации.
-
Вставить сервер конфигурации в приложение, где он настраивает
сам из репозитория Git , вместо того, чтобы работать как отдельное приложение, обслуживающее клиентов. Поэтому некоторые свойства начальной загрузки должны быть установлены и/или аннотация @ EnableConfigServer должна быть удалена, что зависит от варианта использования.
-
Сделать сервер конфигурации доступным в сервисе Spring Netflix Eureka
обнаружение и включение автоматического обнаружения сервера в клиентах конфигурации. Это становится важным, если у сервера нет фиксированного местоположения или он перемещается в своем местоположении.
И в заключение, вы найдете исходный код этой статьи на Github .