Eine Identität für alles mit Keycloak
Seite 2: Einfach und sicher
Red Hat hat sich mit Keycloak zum Ziel gesetzt, den Einsatz für Entwickler im Enterprise-Umfeld so einfach wie möglich zu gestalten. Daher ist mit Keycloak nicht nur ein vollständiger Identity Provider als Open-Source-Software verfügbar, sondern auch unterschiedliche Adapter für die direkte Anbindung von Anwendungen in JavaScript oder Java unter anderem zum Verwenden mit JSF, Spring Boot und Applikationsservern wie JBoss/Wildfly und Tomcat. Damit können Entwickler Keycloak im simpelsten Fall rein konfigurativ implementieren, indem sie den passenden Adapter auf dem für die Applikation verwendeten Anwendungsserver installieren und eine Verbindung mit der Keycloak-Instanz über entsprechende Konfigurationen herstellen. Der weitere Artikel zeigt den groben Ablauf der Installation und die Anbindung von Keycloak an eine existierende Spring-Boot-Applikation.
Zur Installation von Keycloak laden Entwickler die Standalone-Server-Distribution von der offiziellen Homepage herunter. Nach dem Entpacken erhalten sie einen vollständig konfigurierten Wildfly Application Server, den sie über das Skript ...\bin\standalone.bat beziehungsweise .../bin/standalone.sh starten. Anschließend erreichen sie über die URL http://localhost:8080/auth die Startseite von Keycloak, um dort zunächst ein Admin-Konto zu erstellen und dafür Benutzername und Passwort zu vergeben. Im Anschluss daran loggen sie sich mit den Credentials in die Admin-Oberfläche unter http://localhost:8080/auth/admin ein (vgl. Abb. 2).
(Bild: Keycloak)
An dieser Stelle können Entwickler bereits damit beginnen, Keycloak über die Admin-Oberfläche für die abzusichernde Spring-Boot-Applikation zu konfigurieren. Dazu erstellen sie im ersten Schritt einen sogenannten Realm (Reich). Realms sind in sich geschlossene Mengen von Benutzern, Credentials, Rollen und Gruppen. Sie erlauben es, mehrere Benutzerbereiche völlig isoliert voneinander innerhalb einer Keycloak-Instanz zu verwalten.
Anschließend folgt das Anlegen eines neuen Clients. Dafür ist zunächst die Definition einer Redirect-URL notwendig – für die restlichen Einstellungen lassen sich zunächst die Standardwerte übernehmen. Keycloak leitet die Benutzer nach erfolgreicher Authentifizierung an die Redirect-URL weiter. Die für den Client einzutragende URL ist im konkreten Fall die der Spring-Boot-Applikation. Damit sind alle Schritte erledigt, die auf Keycloak-Seite zur Konfiguration eines neuen Clients nötig sind.
Die folgenden Absätze widmen sich der Anpassung der Spring-Boot-Applikation und gehen davon aus, dass die abzusichernde Spring-Applikation bisher durch kein Framework abgesichert ist. Wer die Anwendung beispielsweise bereits mit Spring Security geschützt hat, kann einen Spring-Security-Adapter verwenden. Das Beispiel zeigt jedoch den Einsatz des Spring-Boot-Adapters. Dazu ist zunächst die entsprechende Abhängigkeit notwendig – folgender Codeauszug setzt auf Gradle als Build-Werkzeug. Durch das Hinzufügen der Dependency erkennt Spring Boot beim Starten der Anwendung, dass es Keycloak verwenden soll, und setzt automatisch die benötigten Konfigurationen:
dependencies {
(...)
compile('org.keycloak:keycloak-spring-boot-starter')
(...)
}
Im zweiten Schritt müssen Entwickler den hinzugefügten Adapter in application.properties konfigurieren. Dazu geben sie die URL zur Startseite von Keycloak, den Namen des neu angelegten Realm und den Namen des darin angelegten Clients an. Weiterhin setzen sie den Wert keycloak.public-client auf true. Diese Einstellung gilt für Webseiten beziehungsweise Clients, die nicht in der Lage sind, ein Secret sicher zu verwahren – beispielsweise reine JavaScript-Webseiten. Für diese greifen andere Mechanismen, um den Client dennoch erfolgreich in Keycloak zu authentifizieren. Weitere Details beziehungsweise Konfigurationen lassen sich der offiziellen Dokumentation entnehmen.
Die beiden letzten Zeilen spezifizieren die für Java EE typischen Security Constraints. Das folgende Listing legt somit fest, dass nur Benutzer mit der Rolle user auf die Seiten gelangen, deren URL auf das Pattern /products/* passt:
keycloak.auth-server-url=http://localhost:8080/auth
keycloak.realm=TestRealm
keycloak.public-client=true
keycloak.resource=TestClient
keycloak.security-constraints[0].authRoles[0]=user
keycloak.security-constraints[0].securityCollections[0].patterns[0]=
/products/*
Nach den beiden Schritten ist die Anwendung erfolgreich als Client an Keycloak gebunden. Sobald Benutzer nun innerhalb der Spring-Boot-Applikation eine URL aufrufen, die auf das beschriebene Pattern passt, verweist die Anwendung sie an die angebundene Keycloak-Instanz, um sich dort anzumelden. Erst anschließend erfolgt die Weiterleitung an die gewünschte Seite. Zum Testen müssen Entwickler nur noch einen Benutzer mit der Rolle user in Keycloak anlegen. Analog können sie weitere Rollen oder Security Constraints setzen.
Wer innerhalb der Spring-Boot-Anwendung auf die Identität des angemeldeten Benutzers eingehen möchte, kann das ID-Token aus dem Request ziehen und damit alle übergebenen Informationen bequem laden. Beispielsweise lässt sich der Benutzernamen auslesen und innerhalb der Anwendung an passender Stelle platzieren, wie folgendes Listing zeigt:
KeycloakSecurityContext securityContext =
((KeycloakPrincipal) request.getUserPrincipal())
.getKeycloakSecurityContext();
IDToken idToken = securityContext.getIdToken();
String userName = idToken.getName();
AccessToken accessToken = securityContext.getToken();
Set<String> userRoles =
accessToken.getRealmAccess().getRoles();
Ebenso bequem können Entwickler das Access-Token und die darin enthaltenen Benutzerrollen aus dem Request laden. Mit den Rollen können sie innerhalb einer Seite beispielsweise bestimmte Buttons oder Labels nur für Benutzer mit der Rolle admin sichtbar schalten.
Die Administration von Keycloak erfolgt über ein Webfrontend, dessen Oberfläche alle benötigten Einstellungen übersichtlich anzeigt. Optional lassen sich das Admin CLI oder eine REST API zur Administration verwenden. Wer Keycloak aus einer Java-Anwendung heraus administrieren oder manuelle Aufgaben programmatisch vereinfachen möchte, kann eine bestehende Java-Client-Bibliothek nutzen, die im Hintergrund die REST API aufruft. Das System bietet somit weitreichende Mechanismen zur Automatisierung der Administration.
Stateless mit JSON Web Token
OpenID Connect und damit auch Keycloak basieren weitgehend auf JSON Web Token (JWT). Der offene Standard zum Erstellen von Access-Tokens soll vor allem die Integrität von Anfragen sicherstellen. Dazu wird das Token in drei Elemente geteilt, die durch einen Punkt voneinander getrennt sind. Die folgende Abbildung zeigt auf der linken Seite das Token und rechts die dekodierte Version.
(Bild: JWT.io)
Der eigentliche Inhalt des JWT (Payload) steckt im mittleren Teil, der die Beschreibung der sogenannten Claims als Key/Value-Paare enthält. Im OpenID-Connect-Kontext sind an der Stelle beispielsweise der Name oder die E-Mail des Benutzers aufgeführt. Im ersten Teil des JWT finden sich der Header und damit der Algorithmus zum Signieren und/oder Verschlüsseln des Tokens sowie der Tokentyp, der bei JWT stets jwt ist. Das Ergebnis des Signierens steht im letzten der drei Teile, der Signatur. Sie wird aus dem Header, dem Payload und einem Secret berechnet, das nur der Server kennt. Die Vorgehensweise stellt sicher, dass die enthaltenen Claims valide sind und niemand die Payload eines JWT verändert beziehungsweise manipuliert hat.
Der Server, der die JWTs entgegennimmt, prüft nur noch die Validität des Tokens und gewährt den Benutzern im Erfolgsfall Zugriff auf die angeforderten Ressourcen. Somit müssen die Betreiber der Anwendung nicht mehr unterschiedliche Sessions für die individuellen Benutzer auf dem Server halten und können die Infrastruktur völlig stateless betreiben. Damit sparen sie serverseitig Speicher und reduzieren die Anzahl der Datenbankzugriffe. Der Preis dafür ist eine höhere Anforderung an die Rechenleistung, da das System die JWTs jeweils einzeln validieren muss. Wer mehr über JWT lernen möchte und wissen will, warum sie sich besonders gut für Microservice-Architekturen eigeen, findet in einem Talk von Hubert Sablonnière nützliche Informationen: