Blog

Fast Internet

Spring-Boot-Service mit HTTP/2

Bild: Designed by starline / Freepik

Im vorigen Artikel wurde gezeigt, wie wir einen Spring-Boot-Service für das unverschlüsselte http/2-Cleartext (h2) aufsetzen. Jetzt wird es darum gehen, einen Service mit echtem http/2 aufzusetzen.

Echt heißt in diesem Fall, inklusive der obligatorischen Verschlüsselung des Datenverkehrs mit TLS bzw. SSL.

Dafür muss etwas mehr Aufwand betrieben werden. Dieser Artikel erklärt, was im Einzelnen zu tun ist.

Voraussetzungen: Spring mit http/2

Was ist zu tun, um einen Spring-Service http/2-tauglich zu machen?

  1. Der Service muss mit einer ConnectionFactory ausgestattet werden, die http/2 beherrscht. Wir nutzen dafür in diesem Beispiel den Webserver Undertow, der prima mit Spring zusammenarbeitet.
  2. Der Service muss mit einem gültigen TLS-Zertifikat ausgestattet werden. Wir hinterlegen dieses in einem Java Keystore.
  3. Die JVM, in welcher der Service ausgeführt wird, muss in ihrem Boot-Classpath eine JAR-Datei liegen haben, in der die TLS-Erweiterung ALPN implementiert wird.

Es folgen die Details zu den einzelnen Punkten. Ganz am Ende dieses Artikel ist ein Link auf ein Github-Repository aufgeführt, das ein lauffähiges Spring-Boot-Projekt enthält.

1. Eine ConnectionFactory für http/2

Unseren Spring-Service mit einer http/2-tauglichen ConnectionFactory auszustatten, erfordert nur einige Zeilen Code:

@EnableAutoConfiguration
@SpringBootApplication
public class App {
  
  public static void main(String[] args) {
    SpringApplication.run(App.class, args);
  }

  @Bean
  UndertowEmbeddedServletContainerFactory embeddedServletContainerFactory() {
    
    UndertowEmbeddedServletContainerFactory factory = //
        new UndertowEmbeddedServletContainerFactory();
    factory.addBuilderCustomizers(
        builder -> builder
            .setServerOption(UndertowOptions.ENABLE_HTTP2,
                             true)
            .setServerOption(UndertowOptions.HTTP2_SETTINGS_ENABLE_PUSH,
                             true)
    );
    return factory;
  }
}

2. SSL- bzw. TLS-Zertifikat

HTTP/2 funktioniert nur über HTTPS, weswegen wir für unseren Service eine entsprechendes Zertifikat brauchen. Für dieses Beispiel können wir mit dem keytool aus dem JVM selbst eines erstellen.

Eine ausführliche Anleitung dazu ist bei Oracle zu finden

$ keytool -genkey -alias sampleKeyAlias \
  -keyalg RSA -keypass changeit \
  -storepass changeit \
  -keystore sample.jks

Bei einem Zertifikat für den Hostnamen localhost geben wir folgende Werte an:

Enter keystore password: secret
What is your first and last name?
[Unknown]: localhost
What is the name of your organizational unit?
[Unknown]: ACME
what is the name of your organization?
[Unknown]: ACME Corp
What is the name of your City or Locality?
[Unknown]: Hamburg
What is the name of your State or Province?
[Unknown]: Hamburg
What is the two-letter country code for this unit?
[Unknown]: DE
Is<CN=localhosr, OU=ACME, O=ACME Corp, L=Hamburg,
 ST=Hamburg, C=DE> correct?
[no]: yes

Enter key password for <client>
    (RETURN if same as keystore password):

Wir haben nun eine Datei mit dem Namen sample.jks, in der unser selbst erstelltes Zertifikat enthalten ist. Unsere Anwendung konfigurieren wir so, dass sie die Datei sample.jks auf dem Classpath findet. Dazu legen wir eine springkonforme Datei application.properties an, die folgenden Inhalt hat:

server.port                   = 8443
server.ssl.key-store          = classpath:sample.jks
server.ssl.key-store-password = secret
server.ssl.key-password       = secret

ALPN für den Service

ALPN steht für Application-Layer Protocol Negotiation. Diese Erweiterung von TLS ermöglicht es HTTP/2-Server und -Client, die Protokoll-Version untereinander auszuhandeln.

Damit ein auf Java basierender Spring-Boot-Service ALPN durchführen kann, muss ein entsprechendes JAR-Archiv zur Verfügung gestellt werden. Ein für Java-8-Anwendungen passendes Jar gibt es im Maven-Repository.

Es reicht jedoch nicht, dieses Jar in den normalen Classpath zu legen oder sie als Maven-Dependency einzubinden. Stattdessen muss diese Jar-Datei beim Starten der Application zum sogenannten Boot-Classpath hinzugefügt werden. Dafür verwendet man den Parameter -Xbootclasspath, wie im folgenden Beispiel, wenn man die Applikation simple-rest-service-1.0-SNAPSHOT.jar damit starten möchte:

$ java -Xbootclasspath/p:./lib/jetty-alpn-agent-2.0.5.jar \
  -jar target/simple-rest-service-1.0-SNAPSHOT.jar

Alles zusammenführen

In einem Projekt bei Github sind alle oben aufgeführten Punkte in einem lauffähigen Spring Boot Service zusammengeführt. Dieses verwendet einen einfachen RESTful Webservice, wie er im Spring Boot Tutorial beschrieben ist. Er wurde mit einer HTTP/2-ConnectionFactory ausgestattet und kann mit ALPN-Unterstützung gestartet werden. Näheres erläutert die Readme-Datei des Projekts.

Fazit

Es ist einiges an Aufwand nötig, um einen RESTful-Webservice mit Java 8 und Spring Boot aufzusetzen, der darüber hinaus noch HTTP/2 beherrschen soll.

Die zukünftige Version 8 des Java-EE-Standards erhält mit der Servlet-4.0-Spezifikation endlich HTTP/2-Unterstützung. Es bleibt die Hoffnung, dass uns Application-Container wie WildFly oder JBoss beziehungsweise Tomcat damit das Leben etwas einfacher machen.

Die fertige Spezifikation des Java-EE-8-Standards ist derzeit für Ende 2017 angekündigt. Die Implementierungen durch Applicationserver-Projekte folgen hoffentlich zeitig danach.

Über den Autor

Antwort hinterlassen