Keycloak und Spring Security Teil 1: Einrichtung & Frontend

3 Kommentare

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/admin/ 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.

keycloak-realm-config

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

keycloak-frontend-app-config

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

keycloak-mail-clientconfig

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.

add-role-to-user

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.

project-structure-frontend-png

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:

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

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

$(document).ready(function() {
 
var keycloak = Keycloak({
        url: 'http://localhost:8080/auth',
        realm: 'springboot-example',
        clientId: 'guestbook-frontend-app'
    });
 
...
keycloak.init({ onLoad: 'login-required'
}).success(
    function(){
        console.log('authenticated!');
        getEntries();
    })
    .error(
        function(){
            console.log('error');
    });
...
});

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:

var getEntries = function(){
        console.log('token:' + keycloak.token);
 
        $.ajax({
            url:"http://127.0.0.1:8090/guestbook",
            method:"GET",
            headers: {
                "Authorization": "Bearer " + keycloak.token
            },
            success:function(data) {
                //anzeigeLogik...
            },
            error:function() {
                console.log("error");
            }
        });
    };

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:

$("#send").on("click", function() {
 
	var data = {
		title:$("#title").val(),
		comment:$("#comment").val(),
		commenter:$("#commenter").val()
	};
 
	$.ajax({
		url:"http://127.0.0.1:8090/guestbook",
		method:"POST",
        headers: {
            "Authorization": "Bearer " + keycloak.token
        },
        contentType:"application/json",
		data:JSON.stringify(data),
		success:function() {
			$(".successarea").show();
			$("form").hide();
		},
		error:function() {
			console.log("Etwas hat beim hinzufügen nicht geklappt!");
		}
	});
});

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 (TODO)

Dominik arbeitet als erfahrener Software-Entwickler und Consultant im Bereich verteilte Systeme. Er unterstützt seine Kunden in der Entwicklung verteilter Systeme, sei es bei der Entwicklung von RESTful APIs und Webapplikationen oder der Entwicklung von Microservice- und Big-Data-Architekturen.

Share on FacebookGoogle+Share on LinkedInTweet about this on TwitterShare on RedditDigg thisShare on StumbleUpon

Kommentare

  • Jasper Sprengers

    4. September 2017 von Jasper Sprengers

    Great post! I’m looking forward to the sequels. I noticed there’s also a Keycloak module for angular 2. Very cool developments.

    • Dominik Guhr

      4. September 2017 von Dominik Guhr

      Thank you very much, Jasper!
      Part 2 is nearly done and I’m looking forward to release it at sometime this week.

      You are right, there is a very nice Keycloak-Module for angular 2, too. But to be honest I first wanted to create only one blogpost covering everything from frontend to inter-service-communication, which now happens to be at least three posts.
      So back then I decided to use the minimal frontend approach for simplicities sake – apart from me personally being a fan of more minimal JS/TS-Frameworks such as vue.js or even backbone.js 😉

Kommentieren

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.