//

Keycloak und Spring Security Teil 1: Einrichtung & Frontend

3.9.2017 | 8 Minuten Lesezeit

Wie lässt sich Keycloak als Identity- und Accessmangement-Provider in aktuelle Frameworks, speziell verteilte Spring Boot-Services, einbinden? Dazu am besten noch so in Spring Security integrieren, dass man nicht alles neu-erlernen oder umschreiben muss? Oder auch: Yay, jetzt haben wir endlich Microservices – aber wie können wir sie vernünftig absichern?

Ähnliche Fragen habe ich in letzter Zeit häufiger gehört. „Eigentlich gar nicht so schwer“ hat als Antwort dabei oft nicht ausgereicht. Da dachte ich mir, versuche ich es mal mit dieser Artikelserie. Dabei möchte ich euch im Hands-On Format dabei helfen, die Möglichkeiten und Fallstricke bei der Integration von Keycloak in Spring Security und in eine (zugegebenermaßen sehr naiv gehaltene) Microservices-Landschaft zu erkunden. Natürlich stelle ich am Ende jedes Teils ein entsprechendes Codebeispiel bereit (tl;dr darf ab hier dann jetzt nach unten scrollen).

Nachdem meine Kollegen Dieter  und Jannik mit ihren Beiträgen schon die Konzepte und Standards hinter Red Hat’s Open-Source IAM-Lösung Keycloak beleuchtet und einen guten Überblick über die Einrichtung und den Funktionsumfang gegeben haben, werde ich diese Dinge also nur am Rande betrachten. Ich kann euch aber die Lektüre der Posts nur dringend ans Herz legen, da gerade beim Thema Sicherheit ein Verständnis der zugrunde liegenden Konzepte essenziell ist. Auch die sehr gute Dokumentation von Keycloak selbst ist immer einen Blick wert.

Disclaimer : Grundsätzlich habe ich versucht, das Beispiel so einfach wie möglich zu halten, damit der Fokus auf die Integration von Keycloak in eine Webapplikation und Spring Boot / Spring Security über mehrere Server hinweg gewahrt bleibt. Dazu habe ich durchaus wichtige Aspekte wie die Grundeinrichtung von Keycloak, die Nutzung von Reverse Proxies, Service Discovery, JS-Frameworks etc. oder auch die zusätzliche Absicherung mittels CSRF-Tokens bewusst aus dem Scope der Beiträge entfernt. Ich gehe weiter davon aus, dass Keycloak (3.2.1 Final) schon heruntergeladen und lauffähig ist.

Das Hands-On Szenario

Im Laufe der Artikelserie werden wir zusammen eine kleine Gästebuch-Applikation mit drei Modulen (folgend „App/Apps“) auf Spring-Boot-Basis erstellen und diese mit Keycloak absichern. Teil 1 befasst sich dabei mit der Keycloak-Einrichtung für das Szenario und der Anbindung eines Logins via JS-Adapter im Frontend. In Teil 2 befasse ich mich dann zuerst mit der Integration von Keycloak in Spring Security und abschließend mit der (auch asynchronen) Client zu Client-Kommunikation mit dem KeycloakRestTemplate. Also, Los geht’s!

Unser Gästebuch besteht aus folgenden Spring Boot-basierten Apps:

Die Frontend-App:

  • beinhaltet ein bewusst einfach gehaltenes JS/HTML-Frontend unter Nutzung von jquery & bootstrap
  • nutzt den Keycloak Javascript-Adapter
  • login via openid-connect & public OIDC Auth Flow mit Weiterleitung an den Keycloak-Realm

Die Backend-App:

  • bietet Endpunkte an, die es allen Nutzern mit der in Keycloak zugewiesenen Rolle „user“ erlauben, Einträge anzuzeigen & anzulegen
  • nutzt Spring Security mit Keycloak-Integration zur Absicherung
  • ruft via @Async asynchron die Mail-App (s.u.) über das KeycloakRestTemplate auf
  • ist als bearer-only konfiguriert, bietet also keinen eigenen Login an.

Die Mail-App:

  • Verschickt Mails an eine festgelegte Adresse, sobald ein Eintrag hinzugefügt wird
  • besitzt ebenfalls geschützte Endpunkte wie bei der Backend-App über eine rollenbasierte Zugriffskontrolle
  • ist ebenfalls als bearer-only konfiguriert

Keycloak:

  • Es wird Keycloak als Standalone-Server wie in der Dokumentation beschrieben in Version 3.2.1 Final genutzt.

Keycloak-Einrichtung

So ganz ohne Einrichtung geht’s dann doch nicht. Als Server nutzen wir Keycloak standalone in Version 3.2.1. Also ab ins bin-Verzeichnis, ./standalone.sh eingeben und auf geht’s.
Die Admin-Oberfläche ist dabei standardmäßig unter http://localhost:8080/auth/ zu erreichen, der Server entsprechend auf Port 8080 verfügbar. Beim ersten Aufruf muss dabei ein Passwort für den Administrator-Account festgelegt werden.

Der Realm

Sind wir als Admin eingeloggt, legen wir zuerst einen neuen Realm springboot-example an. Die übrigen Einstellungen lassen wir einfach auf den Standardwerten, sie sind für das Beispiel irrelevant.

Bild 1: Übersicht der Keycloak-Realm Config

Die Clients

Weiter geht es mit den Clients. Hier legen wir die Clients guestbook-frontend-app, guestbook-backend-app und guestbook-mail-app mit folgenden Einstellungen an:

Frontend-App Clientkonfiguration

Bild 2: Keycloak Konfiguration der Frontend-App

Unser Frontend nutzt openid-connect mit dem Access Type public. Das heißt, hier wird kein secret mit dem (unsicheren Browser-) Client ausgetauscht. Die Dokumentation hierzu:

Public access type is for client-side clients that need to perform a browser login. With a client-side application there is no way to keep a secret safe. Instead it is very important to restrict access by configuring correct redirect URIs for the client.

Als Valid Redirect URI tragen wir hier http://localhost:8081/*, unseren Frontend-Host, ein. Das Sternchen bezeichnet eine Wildcard, also sind alle URI unter localhost:8081 als valide Redirectziele anzusehen, auf die nach erfolgtem login umgeleitet werden darf.
Wichtig ist daneben, dass wir unter Web Origins ebenfalls unseren Frontend-Host http://localhost:8081 eintragen, um Cross Origin-Requests von diesem Host entgegennehmen zu können (nähere Infos zu CORS gibt es z.B. hier )

Backend- und Mail-App Clientkonfiguration

Bild 3: Bearer-Only Config der Mail-App

Das Backend unseres Gästebuchs nutzt ebenfalls openid-connect. Allerdings ändern wir hier den Access Type wie in Bild 3 zu sehen auf bearer-only, um einen direkten Login an dieser Stelle nicht zu ermöglichen (der findet schon im Frontend statt). Die Konfiguration der Mail-App unterscheidet sich bis auf den Namen nicht von der der Backend-App.

An dieser Stelle möchte ich euch kurz den Teil der Dokumentation über OpenID Connect und speziell den Absatz über den Authorization Code Flow ans Herz legen, die ich hier nur am Rande behandle.

Anlegen einer neuen Rolle und zuweisen zu einem in Keycloak angelegten User

So weit, so gut. Jetzt benötigen wir nur noch beispielhaft eine Rolle user, die wir später zur Authentifizierung an den Endpunkten verwenden wollen.

Bild 4: Zum Hinzufügen der Rolle zum User diese auswählen und auf „Add selected“ klicken.

Dazu legen wir einfach über Roles -> Add Role eine Rolle mit dem entsprechenden Namen an. Zu guter letzt fehlt uns dann noch ein User mit der zugewiesenen Rolle. Dazu legen wir erst einen neuen Nutzer über Users -> Add User an. Hier reicht es, einen Username anzugeben. Nach dem Anlegen wechseln wir in den Reiter Role Mappings und weisen dem angelegten Nutzer wie in Bild 4 zu sehen die Rolle zu.

Das Guestbook-Frontend

Mit der Einrichtung von Keycloak für unser Szenario sind wir jetzt schon fertig. Kommen wir zum ersten etwas spannenderen Teil, der Integration von Keycloak in die Clients. Wir fangen in diesem Teil mit der Erstellung eines minimalen Frontends an, das via Javascript-Adapter an Keycloak angebunden wird.

Bild 5: Projektstruktur des Frontend-Projekts

Unser Frontend wird später über die Einstellung in application.properties auf Port 8081 erreichbar sein und besteht nur aus drei HTML- und zwei JS-Dateien. Okay, das stimmt nicht ganz, diverse Referenzen auf Javascript-Bibliotheken fehlen noch. Aber dazu später mehr.

Den vollständigen Code für die Frontend-App findet ihr hier bei GitHub . Folgend werden wir uns nur die relevanten Codeteile ansehen, um den Rahmen dieses Artikels nicht zu sprengen. Falls euch noch etwas wichtiges fehlt, schreibt aber gern einen Kommentar. Ich versuche dann, es nachzureichen. Zum Start der App reicht es, mvn spring-boot:run einzugeben.

Um die vorhandenen Einträge im Gästebuch einzusehen und selbst welche erstellen zu können möchten wir nun, dass unser Nutzer vor Ansicht der Einträge auf die von Keycloak gestellte Loginseite weitergeleitet wird um sich einzuloggen. Dazu binden wir zuerst eine Referenz auf den Clientadapter für Javascript in den head der zu schützende Seite ein:

1<script src="http://localhost:8080/auth/js/keycloak.js"></script>
2

Danach reicht z.B. folgender Javascript-Code, um den Adapter einzubinden:

1$(document).ready(function() {
2 
3var keycloak = Keycloak({
4        url: 'http://localhost:8080/auth',
5        realm: 'springboot-example',
6        clientId: 'guestbook-frontend-app'
7    });
8 
9...
10keycloak.init({ onLoad: 'login-required'
11}).success(
12    function(){
13        console.log('authenticated!');
14        getEntries();
15    })
16    .error(
17        function(){
18            console.log('error');
19    });
20...
21});
22

In obigem Codesnippet setzen wir zuerst die Adapter-Werte auf die Daten für unsere Keycloak-Instanz, des entsprechenden Realms und der clientId. Ziemlich straight-forward. Danach wird der Adapter initialisiert.
Dabei geben wir die Vorgabe, dass hier ein Login benötigt wird um weitere Schritte zu unternehmen, via onLoad: ‚login-required‘ an.

Das war es auch schon fast – Wenn ihr das Beispiel startet, solltet ihr sehen, dass ihr nach einem Klick auf „Zum Gästebuch“ direkt auf die Keycloak-Loginseite weitergeleitet werdet.

Aber halt, etwas fehlt natürlich noch: Wie rufe ich jetzt einen (hoffentlich bald) geschützten Endpunkt eines anderen Services auf? Nun, dem ein oder anderen mag schon der getEntries() -Call oben aufgefallen sein. Diese function ruft nun genau so einen Endpunkt auf:

1var getEntries = function(){
2        console.log('token:' + keycloak.token);
3 
4        $.ajax({
5            url:"http://127.0.0.1:8090/guestbook",
6            method:"GET",
7            headers: {
8                "Authorization": "Bearer " + keycloak.token
9            },
10            success:function(data) {
11                //anzeigeLogik...
12            },
13            error:function() {
14                console.log("error");
15            }
16        });
17    };
18

Hier holen wir unter /guestbook bei einem geschützten GET-Endpunkt unseres (noch zu erstellenden) Backends alle bisher vorhandenen Einträge des Gästebuchs ab. Weiter greifen wir auf den vom Keycloakadapter zur Verfügung gestellten Token zu und schicken diesen im Authorization-Header mit zum Backend, das wir in Teil 2 dieser Serie implementieren werden.

Ganz ähnlich gestaltet es sich der Ajax-Request beim Hinzufügen neuer Beiträge in der Datei add.js:

1$("#send").on("click", function() {
2 
3    var data = {
4        title:$("#title").val(),
5        comment:$("#comment").val(),
6        commenter:$("#commenter").val()
7    };
8 
9    $.ajax({
10        url:"http://127.0.0.1:8090/guestbook",
11        method:"POST",
12        headers: {
13            "Authorization": "Bearer " + keycloak.token
14        },
15        contentType:"application/json",
16        data:JSON.stringify(data),
17        success:function() {
18            $(".successarea").show();
19            $("form").hide();
20        },
21        error:function() {
22            console.log("Etwas hat beim hinzufügen nicht geklappt!");
23        }
24    });
25});
26

Hier lesen wir bei Klick auf den Senden-Button die Werte der entsprechenden Formularfelder aus und schicken wieder einen Ajax-POST-Request an unser noch zu implementierendes Backend, der einen Authorizationheader beinhaltet. Das war es auch schon, das Frontend ist nun mit einem von Keycloak bereitgestellten Login versehen, die Stubs für die Backendanbindung sind da und wir sind fertig.

Fazit

In Teil 1 dieser Artikelserie haben wir gesehen, wie einfach wir Keycloak mit openid-connect einrichten und mit dem entsprechenden Adapter in eine Webseite integrieren können. Bisher denke ich absolut kein Hexenwerk. In Teil 2 werden wir dafür sorgen, dass auch unser Backend und der Mailservice von Keycloak und Spring Security geschützt kommunizieren können. Bis dahin danke fürs lesen und hinterlasst gern einen Kommentar!

Code: GitHub
Weitere Artikel der Serie: Teil 2 | Teil 3

Beitrag teilen

Gefällt mir

0

//

Weitere Artikel in diesem Themenbereich

Entdecke spannende weiterführende Themen und lass dich von der codecentric Welt inspirieren.

//

Gemeinsam bessere Projekte umsetzen

Wir helfen Deinem Unternehmen

Du stehst vor einer großen IT-Herausforderung? Wir sorgen für eine maßgeschneiderte Unterstützung. Informiere dich jetzt.

Hilf uns, noch besser zu werden.

Wir sind immer auf der Suche nach neuen Talenten. Auch für dich ist die passende Stelle dabei.