Schnelle Einführung in die Konfiguration der Spring Cloud

Schnelleinstieg in die Spring Cloud-Konfiguration

1. Überblick

Spring Cloud Config ist der Client / Server-Ansatz von Spring zum Speichern und Bereitstellen verteilter Konfigurationen in mehreren Anwendungen und Umgebungen.

Dieser Konfigurationsspeicher ist idealerweise unter der Versionskontrolle vonGitversioniert und kann zur Laufzeit der Anwendung geändert werden. Während es sehr gut in Spring-Anwendungen passt, die alle unterstützten Konfigurationsdateiformate zusammen mit Konstrukten wieEnvironment,PropertySource or @Value verwenden, kann es in jeder Umgebung verwendet werden, in der jede Programmiersprache ausgeführt wird.

In diesem Artikel konzentrieren wir uns auf ein Beispiel für die Einrichtung eines vonGit unterstützten Konfigurationsservers, die Verwendung auf einem einfachenREST-Anwendungsserver und die Einrichtung einer sicheren Umgebung mit verschlüsselten Eigenschaftswerten.

2. Projekteinrichtung und Abhängigkeiten

Um sich auf das Schreiben von Code vorzubereiten, erstellen wir zuerst zwei neueMaven-Projekte. Das Serverprojekt stützt sich auf das Modulspring-cloud-config-server sowie auf die Starterpaketespring-boot-starter-security undspring-boot-starter-web:


    org.springframework.cloud
    spring-cloud-config-server
    1.1.2.RELEASE


    org.springframework.boot
    spring-boot-starter-security
    1.4.0.RELEASE


    org.springframework.boot
    spring-boot-starter-web
    1.4.0.RELEASE

Für das Client-Projekt benötigen wir jedoch nur diespring-cloud-starter-config und diespring-boot-starter-web modules:


    org.springframework.cloud
    spring-cloud-starter-config
    1.1.2.RELEASE


    org.springframework.boot
    spring-boot-starter-web
    1.4.0.RELEASE

3. Eine Konfiguration des Konfigurationsservers

Der Hauptteil der Anwendung ist eine Konfigurationsklasse - genauer gesagt ein@SpringBootApplication -, die alle erforderlichen Einstellungen über dieauto-configure-Annotation@EnableConfigServer: abruft

@SpringBootApplication
@EnableConfigServer
public class ConfigServer {

    public static void main(String[] arguments) {
        SpringApplication.run(ConfigServer.class, arguments);
    }
}

Jetzt müssen wir den Serverport konfigurieren, auf dem unser Server empfangsbereit ist, und eineGit-URL, die unseren versionierten Konfigurationsinhalt bereitstellt. Letzteres kann mit Protokollen wiehttp,ssh oder einem einfachenfile in einem lokalen Dateisystem verwendet werden.

Tip: Wenn Sie mehrere Konfigurationsserverinstanzen verwenden möchten, die auf dasselbe Konfigurationsrepository verweisen, können Sie den Server so konfigurieren, dass Ihr Repo in einen lokalen temporären Ordner geklont wird. Beachten Sie jedoch, dass private Repositorys mit Zwei-Faktor-Authentifizierung schwer zu handhaben sind! In diesem Fall ist es einfacher, sie auf Ihr lokales Dateisystem zu klonen und mit der Kopie zu arbeiten.

Es gibt auch einigeplaceholder variables and search patterns zum Konfigurieren der verfügbarenrepository-url; Dies geht jedoch über den Rahmen unseres Artikels hinaus. Wenn Sie interessiert sind, ist die offizielle Dokumentation ein guter Anfang.

Wir müssen auch einen Benutzernamen und ein Passwort für dieBasic-Authentication in unserenapplication.properties festlegen, um ein automatisch generiertes Passwort bei jedem Neustart der Anwendung zu vermeiden:

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. Ein Git-Repository als Konfigurationsspeicher

Um unseren Server zu vervollständigen, müssen wir dasGit-Repository unter der konfigurierten URL initialisieren, einige neue Eigenschaftendateien erstellen und sie mit einigen Werten popularisieren.

Der Name der Konfigurationsdatei setzt sich wie ein normaler Springapplication.propertieszusammen, aber anstelle des Wortes "application" wird ein konfigurierter Name, z. Der Wert der Eigenschaft‘spring.application.name' des Clients wird verwendet, gefolgt von einem Bindestrich und dem aktiven Profil. Zum Beispiel:

$> 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'

Troubleshooting: Wenn Sie auf Authentifizierungsprobleme im Zusammenhang mitssh stoßen, überprüfen Sie~/.ssh/known_hosts und~/.ssh/authorized_keys auf Ihrem SSH-Server!

5. Konfiguration abfragen

Jetzt können wir unseren Server starten. Die von unserem Server bereitgestellteGit-unterstützte Konfigurations-API kann über die folgenden Pfade abgefragt werden:

/{application}/{profile}[/{label}]
/{application}-{profile}.yml
/{label}/{application}-{profile}.yml
/{application}-{profile}.properties
/{label}/{application}-{profile}.properties

In dem sich der Platzhalter{label}auf einen Git-Zweig bezieht,{application}auf den Anwendungsnamen des Clients und{profile}auf das aktuell aktive Anwendungsprofil des Clients.

So können wir die Konfiguration für unseren geplanten Konfigurationsclient abrufen, der unter dem Entwicklungsprofil in Zweigmaster ausgeführt wird, und zwar über:

$> curl http://root:[email protected]:8888/config-client/development/master

6. Die Client-Implementierung

Als nächstes kümmern wir uns um den Kunden. Dies ist eine sehr einfache Client-Anwendung, die aus einemREST-Controller mit einerGET-Methode besteht.

Die Konfiguration zum Abrufen unseres Servers muss in einer Ressourcendatei mit dem Namenbootstrap.application abgelegt werden, da diese Datei (wie der Name schon sagt) sehr früh geladen wird, während die Anwendung gestartet wird:

@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);
    }
}

Zusätzlich zum Anwendungsnamen haben wir auch das aktive Profil und die Verbindungsdetails in unsere bootstrap.properties eingefügt:

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

Um zu testen, ob die Konfiguration ordnungsgemäß von unserem Server empfangen wurde undrole value in unsere Controller-Methode eingefügt wird, wird sie nach dem Booten des Clients einfach gewellt:

$> curl http://localhost:8080/whoami/Mr_Pink

Wenn die Antwort wie folgt lautet, funktionieren unsereSpring Cloud Config Server und ihr Client vorerst einwandfrei:

Hello! You're Mr_Pink and you'll become a(n) Developer...

7. Verschlüsselung und Entschlüsselung

Requirement: Um kryptografisch starke Schlüssel zusammen mit Spring-Verschlüsselungs- und Entschlüsselungsfunktionen zu verwenden, müssen die‘Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy Files' in IhrenJVM. installiert sein. Diese können beispielsweise vonOracle heruntergeladen werden. Befolgen Sie zum Installieren die Anweisungen im Download. Einige Linux-Distributionen bieten über ihre Paketmanager auch ein installierbares Paket an.

Da der Konfigurationsserver das Ver- und Entschlüsseln von Eigenschaftswerten unterstützt, können Sie öffentliche Repositorys als Speicher für vertrauliche Daten wie Benutzernamen und Kennwörter verwenden. Verschlüsselten Werten wird die Zeichenfolge{cipher} vorangestellt und kann durch einen REST-Aufruf des Pfads‘/encrypt' generiert werden, wenn der Server für die Verwendung eines symmetrischen Schlüssels oder eines Schlüsselpaars konfiguriert ist.

Ein zu entschlüsselnder Endpunkt ist ebenfalls verfügbar. Beide Endpunkte akzeptieren einen Pfad mit Platzhaltern für den Namen der Anwendung und ihr aktuelles Profil:‘/*/{name}/{profile}'. Dies ist besonders nützlich, um die Kryptografie pro Client zu steuern. Bevor sie jedoch nützlich werden, müssen Sie einen kryptografischen Schlüssel konfigurieren, was wir im nächsten Abschnitt tun werden.

Tip: Wenn Sie zum Aufrufen der Entschlüsselungs- / Entschlüsselungs-API curl verwenden, ist es besser, die Option–data-urlencode (anstelle von–data/-d) zu verwenden oder den Header 'Content-Type' explizit auf zu setzen ‘text/plain'. Dies stellt einen korrekten Umgang mit Sonderzeichen wie '+' in den verschlüsselten Werten sicher.

Wenn ein Wert beim Abrufen über den Client nicht automatisch entschlüsselt werden kann, wird seinkey mit dem Namen selbst umbenannt, dem das Wort "ungültig" vorangestellt ist. Dies soll beispielsweise die Verwendung eines verschlüsselten Wertes als Passwort verhindern.

Tip: Wenn Sie ein Repository mit YAML-Dateien einrichten, müssen Sie Ihre verschlüsselten und vorangestellten Werte in einfache Anführungszeichen setzen! Bei Eigenschaften ist dies nicht der Fall.

7.1. Schlüsselverwaltung

Der Konfigurationsserver ist standardmäßig aktiviert, um Eigenschaftswerte symmetrisch oder asymmetrisch zu verschlüsseln.

To use symmetric cryptography, Sie müssen lediglich die Eigenschaft‘encrypt.key' inapplication.properties auf ein Geheimnis Ihrer Wahl setzen. Alternativ können Sie die UmgebungsvariableENCRYPT_KEY übergeben .

For asymmetric cryptography können Sie‘encrypt.key' auf einenPEM-codierten Zeichenfolgenwert setzen oder einkeystore für die Verwendung konfigurieren.

Da wir für unseren Demoserver eine hochsichere Umgebung benötigen, haben wir uns für die letztere Option entschieden und einen neuen Schlüsselspeicher generiert, der das SchlüsselpaarRSAenthält, wobei Javakeytoolzuerst verwendet wird:

$> keytool -genkeypair -alias config-server-key \
       -keyalg RSA -keysize 4096 -sigalg SHA512withRSA \
       -dname 'CN=Config Server,OU=Spring Cloud,O=example' \
       -keypass my-k34-s3cr3t -keystore config-server.jks \
       -storepass my-s70r3-s3cr3t

Danach fügen wir den erstellten Schlüsselspeicher zuapplication.propertiesunseres Servers hinzu und führen ihn erneut aus:

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

Im nächsten Schritt können wir den Verschlüsselungsendpunkt abfragen und die Antwort als Wert zu einer Konfiguration in unserem Repository hinzufügen:

$> 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

Um zu testen, ob unser Setup ordnungsgemäß funktioniert, ändern wir die KlasseConfigClientund starten unseren Client neu:

@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);
    }
}

Eine abschließende Anfrage an unseren Kunden zeigt uns, ob unser Konfigurationswert korrekt entschlüsselt wurde:

$> 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. Verwenden mehrerer Schlüssel

Wenn Sie mehrere Schlüssel zum Ver- und Entschlüsseln verwenden möchten, z. B. einen dedizierten für jede bereitgestellte Anwendung, können Sie ein weiteres Präfix in Form von\{name:value} zwischen dem Präfix{cipher} und dem PräfixBASE64hinzufügen ) s-codierter Eigenschaftswert.

Der Konfigurationsserver versteht Präfixe wie\{secret:my-crypto-secret} oder\{key:my-key-alias} fast sofort. Die letztere Option benötigt einen konfigurierten Schlüsselspeicher inapplication.properties. Dieser Schlüsselspeicher wird nach einem passenden Schlüsselalias durchsucht. Zum Beispiel:

user.password={cipher}{secret:my-499-s3cr3t}AgAMirj1DkQC0WjRv...
user.password={cipher}{key:config-client-key}AgAMirj1DkQC0WjRv...

Für Szenarien ohne Keystore müssen Sie ein@Bean vom TypTextEncryptorLocator implementieren, das die Suche übernimmt und für jeden Schlüssel einTextEncryptor-Objekt zurückgibt.

7.3. Verschlüsselte Eigenschaften bereitstellen

Wenn Sie die serverseitige Kryptografie deaktivieren und die Entschlüsselung von Eigenschaftswerten lokal durchführen möchten, können Sie Folgendes inapplication.propertiesIhres Servers einfügen:

spring.cloud.config.server.encrypt.enabled=false

Darüber hinaus können Sie alle anderen Eigenschaften von "encrypt. *" Löschen, um die Endpunkte vonRESTzu deaktivieren.

8. Fazit

Jetzt können wir einen Konfigurationsserver erstellen, um Clientanwendungen eine Reihe von Konfigurationsdateien aus dem Repository vonGitbereitzustellen. Es gibt noch ein paar andere Dinge, die Sie mit einem solchen Server tun können.

Zum Beispiel:

  • Servieren Sie die Konfiguration im FormatYAML oderProperties anstelle vonJSON –, auch mit aufgelösten Platzhaltern. Dies kann nützlich sein, wenn Sie es in Nicht-Spring-Umgebungen verwenden, in denen die Konfiguration nicht direkt einemPropertySource zugeordnet ist.

  • Servieren Sie reine Textkonfigurationsdateien - optional mit aufgelösten Platzhaltern. Dies kann beispielsweise nützlich sein, um eine umgebungsabhängige Protokollierungskonfiguration bereitzustellen.

  • Betten Sie den Konfigurationsserver in eine Anwendung ein, in der er sich selbst aus dem Repository vonGitkonfiguriert, anstatt als eigenständige Anwendung für Clients ausgeführt zu werden. Daher müssen einige Bootstrap-Eigenschaften festgelegt und / oder die Annotation@EnableConfigServer entfernt werden, was vom Anwendungsfall abhängt.

  • Stellen Sie den Konfigurationsserver bei Spring Netflix Eureka Service Discovery zur Verfügung und aktivieren Sie die automatische Servererkennung in Konfigurationsclients. Dies ist wichtig, wenn der Server keinen festen Standort hat oder sich an seinem Standort befindet.

Zum Abschluss finden Sie den Quellcode zu diesem Artikelon Github.