Von Philipp Kürsten auf Donnerstag, 28. März 2019
Kategorie: Application Development

Rest-Schnittstellen absichern mit Java Spring, OAuth2.0 & JSON Web Token

Durch die zunehmende Verwendung des Microservice-Architektur-Patterns ist eine zentrale Instanz zur Verwaltung der Benutzeranmeldeinformationen unabdingbar. Das Autorisierungsprotokoll OAuth 2.0 bietet hierfür im Zusammenspiel mit Java Spring eine perfekte Basis. Dieser Artikel veranschaulicht anhand eines Beispiels, wie sich dies in der Praxis realisieren lässt.

OAuth2.0

Das offene Autorisierungsprotokoll OAuth 2.0 gehört zu den bekanntesten Autorisierungsverfahren der heutigen Zeit. Ziel dieses Protokolls ist es, einem Endnutzer Zugriff zu seinen Ressourcen (bzw. REST-Schnittstellen) zu gewähren.

Das OAuth2.0 Protokoll unterstützt hierbei verschiedene Verfahren zur Autorisierung von Nutzern [1]. Grundsätzlich wird bei den Verfahren immer zwischen dem Resource-Owner, dem Ressource-Server und dem Autorisierungs-Server unterschieden. Der Resource-Owner (oder auch Endnutzer) autorisiert sich mittels Nutzername und Passwort gegenüber dem Autorisierungsserver. Dieser stellt daraufhin ein Access-Token (dt. „Zugriffs-Marke") aus. Mithilfe dieses Access-Tokens kann der Nutzer nun eine Anfrage an den Ressourcenserver stellen.

Der Ressourcenserver prüft daraufhin die Integrität des Access-Tokens und verarbeitet dann den Aufruf. Das in diesem Artikel verwendete „Password-Credentials"-Verfahren wird in der Abbildung 1 genauer dargestellt. Weitere Informationen zu diesem Verfahren lassen sich der Link-Sammlung entnehmen.

JSON Web Token

Innerhalb des OAuth2.0-Verfahrens wird die Beschaffenheit des Access-Tokens nicht weiter erläutert. Als Standard hat sich hier das JSON Web Token etabliert, welches sich für JavaScript Clients, wie beispielsweise Angular- oder React-Anwendungen, sehr gut verarbeiten lässt.

Das JSON Web Token (JWT) ist ein JSON-Objekt, dass laut RFC 7519 [2] als sicherer Weg definiert ist, um Informationen zwischen zwei Parteien auszutauschen. Ein JWT ist eine Aneinanderreihung von Header, Payload und Signatur. Diese drei Bestandteile werden durch einen Punkt ​"." voneinander getrennt und enthalten folgende Informationen:

Für Testzwecke lassen sich diese JSON Web Tokens auch online valideren. Hierfür kann die Webseite von JWT.io [3] verwendet werden.

Architektur

Um einer Microservice-Landschaft gerecht zu werden, wird für dieses Beispiel auf zwei unabhängige Microservices gesetzt. Beide Applikationen verwenden als Grundlage Java und setzten darüber hinaus das Framework Spring ein. Das Spring-Framework [4] hat sich in den letzten
Jahren als De-facto-Standard für die Entwicklung von Micro­services im Bereich Java etabliert und bietet eine Reihe von Tools zur Unterstützung in diesem Architektur-Pattern an. Als Paketmanager wird Maven eingesetzt. Die grund­legenden Abhängigkeiten beider Microservices
können der Abbildung 2 entnommen werden.

Die Applikationen werden in ihren Funktionalitäten wie folgt geschnitten:

Zur Veranschaulichung werden die Anfragen später mittels Postman [1] an den Ressource-Server gestellt. Postman bietet darüber hinaus die Möglichkeit, sich gegenüber einem OAuth-Server automatisch zu autorisieren.

Abb. 2: Grundlegende Abhängigkeiten beider Applikationen (pom.xml)

Der Autorisierungsserver

Als Herzstück dieses Projektes kann der Autorisierungsserver angesehen werden. Dieser ist, wie bereits oben beschrieben, für die Ausstellung und Validierung der JSON Web Tokens verantwortlich. Zur Konfiguration dieser Schnittstellen werden zwei Abhängigkeiten aus dem Spring-Framework verwendet (siehe Abbildung 4):

Neben diesen beiden Abhängigkeiten ist die Konfiguration des Autorisierungsservers denkbar einfach. Um den Server nun vollständig betriebsbereit zu machen, müssen nur noch folgende zwei Konfigurationsdateien erstellt werden:

Abb. 4: Ausschnitt der Abhängigkeiten des Autorisierungsservers (pom.xml)

Security-Konfiguration

Die Security-Konfiguration kann der Abbildung 5 entnommen werden. Diese Konfiguration ist grundsätzlich für die Einrichtung der Nutzer zuständig. Die Klasse ​SecurityConfig erbt hierbei von der Klasse ​WebSecurityConfigurerAdapter. Hierdurch ist es notwendig, die Methode ​configure zu überschreiben, welche die Konfiguration übernimmt. Der in dieser Klasse verwendete ​BcryptPasswordEncoder verfügt über einen Hash-Algorithmus, damit die verwendeten Passwörter nicht im Klartext in der Anwendung verwendet werden.

In diesem Fall wird eine ​InMemoryAuthentication verwendet. Das bedeutet, dass die Autorisierung nicht gegen eine Datenbank stattfindet, sondern zur Laufzeit der Anwendung von eben dieser bereitgestellt wird. In diesem Fall wird nur ein Nutzer mit dem Namen „John" und dem Passwort „password" zugelassen. Des Weiteren wird das Passwort encodiert, um zusätzliche Sicherheit zu schaffen und das Passwort nicht im Klartext abspeichern zu müssen.

Hier ist es ebenso möglich, anstelle der Methode ​InMemoryAuthentication eine Datenbank anzubinden oder eine andere Quelle, wie beispielsweise ein LDAP, zu verknüpfen. Um dieses Beispiel möglichst einfach zu halten, ist hier allerdings keine andere Quelle für Nutzer­informationen verknüpft.

Abb. 5: Security-Konfiguration (SecurityConfig.java)

Autorisierungsserver-Konfiguration

Mithilfe der Konfiguration des Autorisierungsservers werden die verschiedenen Schnittstellen, die auch in Abbildung 3 zu sehen sind, konfiguriert. Die vollständige Implementierung kann der Abbildung 6 entnommen werden.

Die Annotation ​@EnableAuthorizationServer stellt die wichtigste Zeile innerhalb dieser Konfigurationsklasse dar, da durch diese die Applikation grundsätzlich als Autorisierungs-Server verstanden wird und für das OAuth2.0-Protokoll vorbereitet wird. Das Spring-Framework benötigt nun zur Konfiguration des Autorisierungsservers nur noch die notwendigen Informationen für das Protokoll wie Client-ID, Client-Secret sowie das zu verwendenden OAuth2.0-Verfahren. Diese Informationen werden in den Methoden ​configure() übergeben, die aus der Klasse ​AuthorizationServerConfigurerAdapter überschrieben werden.

Die Methode ​configure hat hierbei drei Überladungen mit unterschiedlichen Eingangsparametern. Nachfolgend wird die Funktionalität der drei Methoden kurz beschrieben:

Sind alle Konfigurationen soweit getätigt, lässt sich die Applikation über das Kommando ​mvn spring-boot:run in der Kommandozeile starten.

Abb. 6: Konfiguration des Autorisierungsservers (AuthServerConfig.java).

Der Ressourcen-Server

Der Ressourcen-Server stellt Ressourcen (REST-Schnittstellen) zur Verfügung, die durch den Autorisierungsserver abgesichert sein sollen. Als Beispiel wird hierbei ein einzelner Endpunkt zur Verfügung stehen, der unter ​api/hello erreichbar ist. Dieser Endpunkt wird daraufhin den angemeldeten Nutzer begrüßen. Somit kann sichergestellt werden, dass die Anbindung am Autorisierungsserver funktioniert.

Die Maven-Abhängigkeiten für diese Applikation können der Abbildung 8 entnommen werden. Zum Starten der Anwendung werden für einen Ressourcenserver verschiedene Konfigurationen zum Start der Anwendung erwartet, die in der Abbildung 7 zu finden sind. Diese Konfigurationen werden von der Annotation ​@EnableResourceServer in der Konfigurationsklasse ​ResourceServerConfig erwartet. Diese Klasse stellt alle relevanten Informationen zur Verfügung, um die Anbindung an den Autorisierungsserver bereitzustellen (siehe Abbildung 9). Die Vererbung findet von der Klasse ​ResourceServerConfigurerAdapter statt. Diese benötigt zur Verbindung zum Autorisierungsserver einen Token-Service, welcher durch die notwendigen Verbindungsdaten (Client-ID, Client-Secret sowie die URL des Autorisierungsservers) initialisiert wird.

Des Weiteren ist es notwendig zu definieren, welche Endpunkte durch diese Autorisierung abgesichert werden sollen. Dies wird in der Methode ​configure() definiert. In diesem Beispiel werden alle Endpunkte abgesichert, die mit ​api/ beginnen. Alle weiteren Endpunkte sind ohne Autorisierung erreichbar.

Nun wird nur noch der Endpunkt ​api/hello benötigt. Hierfür wird eine Klasse ​HelloController.java angelegt, der diesen bereitstellt (siehe Abbildung 10). Über das Objekt Principal lässt sich der aktuell eingeloggte Benutzer des eingehenden Requests ermitteln. In diesem Beispiel wird über den Aufruf ​principal.getName() der Name zurückgegeben.

Sind alle Konfigurationen abgeschlossen, lässt sich die Applikation über das Kommando ​mvn spring-boot:run in der Kommandozeile starten.

Abb. 7: Applikations Einstellungen Ressourcenservers (application.yml)

Abb. 8: Ausschnitt der Abhängigkeiten des Ressourcenservers (pom.xml)

Abb. 9: Resource-Server-Konfiguration (ResourceServerConfig.java)

Abb. 10: Principal aus dem Request erhalten (HelloController.java)

Ein kleiner Test

Will man nun die Schnittstelle ​api/hello vom Ressourcenserver aufrufen, so muss zuerst ein Access-Token vom Autorisierungsserver abgerufen werden.

Über den Button ​Get New Access Token kann dies im Postman geschehen, wenn die Autorisierung auf OAuth 2.0 gestellt wird. Im nachfolgenden Dialog müssen, dann die Daten für den Autorisierungsserver eingegeben werden (siehe Abbildung 11). Durch einen Klick auf ​Request Token wird der Autorisierungsserver mittels ​HTTP-POST nach einem Access-Token gefragt. Dieser kann dann im weiteren Verlauf verwendet werden.

Mittels ​HTTP-GET kann dann eine Anfrage an den Ressource-Server gestellt werden (siehe Abbildung 12). Hat alles funktioniert, so sollte als Antwort „Hallo John" zurückgesendet werden.

Beendet man nun den Autorisierungs-Server innerhalb der Kommandozeile (STRG + C), so sollte bei einem wiederholten Aufruf an den Ressourcenserver ein Fehler zurückgegeben werden, dass der mitgelieferte Token nicht validiert werden konnte.

Fazit

Dieser Artikel zeigt anschaulich, wie REST-Schnittstellen innerhalb einer Spring-Applikationen abgesichert werden können. Mittels einfacher Konfigurationsklassen kann so in kürzester Zeit eine zentrale Komponente zur Autorisierung von Nutzern aufgebaut werden.

Ein zentraler Autorisierungsserver kann in einer Microservice-Landschaft für mehrere Applikationen gleichzeitig verwendet werden und die Komplexität von Security-Anforderungen an einer Stelle bündeln. Unter Zunahme weiterer Security-Aspekte wie der Rollenverwaltung lassen sich so verschiedene Schnittstellen für unterschiedliche Nutzergruppen absichern.

Der gesamte Source-Code der Anwendung kann online heruntergeladen werden. [8]

Phillipp Kürsten (info@ordix.de)

Links/Quellen

[1] OAuth 2.0: https://oauth.net/2/

[2] JSON Web Token - Standard: https://tools.ietf.org/html/rfc7519#

[3] JSON Web Token - Encoder: https://jwt.io/

[4] Java - Spring: https://spring.io/

[5] Spring Security OAuth 2: https://projects.spring.io/spring-security-oauth/docs/oauth2.html

[6] Postman – API Development: https://www.getpostman.com/

[7] OAuth 2.0 – Password Flow: https://developer.okta.com/blog/2018/06/29/what-is-the-oauth2-password-grant

[8] GitHub – Beispiel-Projekt: https://github.com/PhilKuer/spring-jwt-oauth2-sample

[Q1] IT-Security – ORDIX Blog: https://blog.ordix.de/component/easyblog/tags/it-security

[Q2] Spring Power Workshop – ORDIX Seminare: https://seminare.ordix.de/seminare/entwicklung/java-ee/spring-power-workshop.html

[Q3] Microservices Workshop mit Spring Boot – ORDIX Seminare: https://seminare.ordix.de/seminare/entwicklung/java-ee/microservices-workshop-mit-spring-boot.html

BILDNACHWEIS © istockphoto.com | Cecilie_Arcurs | Lassen Sie uns eintauchen in diesen code

Kommentare hinterlassen