Keycloak per Custom-API erweitern
Ziel
In dieser Aufgabe erweitern Sie Keycloak, indem Sie eine eigene REST API in Java entwickeln und als Extension in Keycloak einbinden. Sie legen ein Java-Projekt an, kompilieren eine JAR, integrieren diese in ein Docker-Image und starten einen Keycloak-Container mit Ihrer Erweiterung. So lernen Sie, wie Sie Keycloak an individuelle Anforderungen anpassen können.
Hilfsmittel
- Versuchen Sie, die unten stehenden Aufgaben mit Hilfe der Folien eigenständig zu lösen.
- Sollten Sie dabei Probleme haben, finden Sie bei jeder Aufgabe einen ausklappbaren Block, in dem der Lösungsweg beschrieben wird.
Aufgabe 1 - Custom REST API für Nutzer
Zunächst wollen wir eine API für Nutzer erstellen. Dafür müssen wir:
- ein Java-Projekt anlegen
- eine JAR kompilieren
- Keycloak mit dem zusätzlichen Provider starten
1.1 Java-Projekt anlegen
- Führen Sie folgende Befehle im Terminal aus. Dadurch wird die notwendige Ordner-Struktur erzeugt.
cd /home/coder/workspace
mkdir -p custom-spi/src/main/java/de/corewire/keycloak/restapi
touch custom-spi/src/main/java/de/corewire/keycloak/restapi/CustomEndpointProviderFactory.java
touch custom-spi/src/main/java/de/corewire/keycloak/restapi/CustomEndpointProvider.java
mkdir -p custom-spi/src/main/resources/META-INF/services
touch custom-spi/src/main/resources/META-INF/services/org.keycloak.services.resource.RealmResourceProviderFactory
touch custom-spi/pom.xml
touch custom-spi/Dockerfile
- Kopieren Sie den Inhalt der pom.xml und machen Sie sich mit dem Inhalt vertraut.
- Stellen Sie sicher, dass die aktuelle Keycloak-Version verwendet wird.
Inhalt pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>de.corewire.keycloak</groupId>
<artifactId>restapi</artifactId>
<version>0.0.1</version>
<properties>
<keycloak.version>26.1.3</keycloak.version>
</properties>
<dependencies>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-server-spi</artifactId>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-server-spi-private</artifactId>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-services</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-parent</artifactId>
<version>${keycloak.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<finalName>${project.groupId}-${project.artifactId}-${project.version}</finalName>
</build>
</project>
1.2 ProviderFactory anlegen
- Bearbeiten Sie die Datei
CustomEndpointProviderFactory.java
. Aktuell ist sie noch leer. Sie soll:- Die Klasse
CustomEndpointProviderFactory
enthalten - Das Interface
RealmResourceProviderFactory
implmentieren - Die
create
-Funktion soll eineCustomEndpointProvider
-Instanz erstellen und zurückgeben.
- Die Klasse
Tipp (Klicken Sie auf den Pfeil, falls Sie nicht weiterkommen)
package de.corewire.keycloak.restapi;
import org.keycloak.Config.Scope;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.services.resource.RealmResourceProvider;
import org.keycloak.services.resource.RealmResourceProviderFactory;
public class CustomEndpointProviderFactory implements RealmResourceProviderFactory {
}
Lösung (Klicken Sie auf den Pfeil, falls Sie nicht weiterkommen)
package de.corewire.keycloak.restapi;
import org.keycloak.Config.Scope;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.services.resource.RealmResourceProvider;
import org.keycloak.services.resource.RealmResourceProviderFactory;
public class CustomEndpointProviderFactory implements RealmResourceProviderFactory {
@Override
public RealmResourceProvider create(KeycloakSession keycloakSession) {
return new CustomEndpointProvider(keycloakSession);
}
@Override
public void init(Scope scope) {
}
@Override
public void postInit(KeycloakSessionFactory keycloakSessionFactory) {
}
@Override
public void close() {
}
@Override
public String getId() {
return "custom-endpoint";
}
}
1.3 Provider anlegen
- Bearbeiten Sie die Datei
CustomEndpointProvider.java
. Aktuell ist sie noch leer. Sie soll:- Die Klasse
CustomEndpointProvider
enthalten - Das Interface
RealmResourceProvider
implmentieren - Einen Konstruktor enthalten, der einen KeycloakSession als Parameter entgegen nimmt und in einer Instanz-Variable speichert.
- Einen simplen Endpunkt definieren
@GET @Path("hello") public Response hello() { return Response.ok(Map.of("hello", "world")).build(); }
- Die Klasse
Tipp (Klicken Sie auf den Pfeil, falls Sie nicht weiterkommen)
package de.corewire.keycloak.restapi;
import java.util.Map;
import org.keycloak.models.KeycloakSession;
import org.keycloak.services.resource.RealmResourceProvider;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.core.Response;
public class CustomEndpointProvider implements RealmResourceProvider {
}
Lösung (Klicken Sie auf den Pfeil, falls Sie nicht weiterkommen)
package de.corewire.keycloak.restapi;
import java.util.Map;
import org.keycloak.models.KeycloakSession;
import org.keycloak.services.resource.RealmResourceProvider;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.core.Response;
public class CustomEndpointProvider implements RealmResourceProvider {
private final KeycloakSession session;
public CustomEndpointProvider(KeycloakSession session) {
this.session = session;
}
@Override
public Object getResource() {
return this;
}
@Override
public void close() {
}
@GET
@Path("hello")
public Response hello() {
return Response.ok(Map.of("hello", "world")).build();
}
}
1.4 ProviderFactory registrieren
- Als letztes müssen wir die
CustomEndpointProviderFactory
noch alsRealmResourceProviderFactory
registrieren. - Tragen Sie dafür
de.corewire.keycloak.restapi.CustomEndpointProviderFactory
in die Dateiorg.keycloak.services.resource.RealmResourceProviderFactory
ein.
1.5 Compilieren und Keycloak erweitern
- Kopieren Sie den Inhalt vom Dockerfile aus den Folien in die leere
Dockerfile
-Datei. - Passen Sie das Dockerfile entsprechend an
Lösung (Klicken Sie auf den Pfeil, falls Sie nicht weiterkommen)
- Da wir für den Test keine externe Datenbank nutzen, können alle ENV-Variablen gelöscht werden, die die Datenbank konfigurieren
FROM maven:3.9.9-amazoncorretto-23 as builder WORKDIR /user/src/mymaven COPY pom.xml /user/src/mymaven RUN mvn dependency:resolve COPY src /user/src/mymaven/src RUN mvn package FROM quay.io/keycloak/keycloak:latest COPY --from=builder --chown=keycloak:root \ /user/src/mymaven/target/*.jar \ /opt/keycloak/providers RUN /opt/keycloak/bin/kc.sh build
- Bauen Sie das Image
Lösung (Klicken Sie auf den Pfeil, falls Sie nicht weiterkommen)
cd /home/coder/workspace/custom-spi
docker build -t kc-custom-spi .
- Starten Sie das Image mit
docker run --rm --network=proxy --label-file ../labels.txt -e KC_BOOTSTRAP_ADMIN_USERNAME=admin -e KC_BOOTSTRAP_ADMIN_PASSWORD=admin -e KC_PROXY_HEADERS=xforwarded kc-custom-spi start-dev --hostname=https://keycloak.code-0.labs.corewire.de
1.6 Custom REST API testen
- Testen Sie, ob die API funktioniert.
- Wurde der Provider geladen?
Lösung (Klicken Sie auf den Pfeil, falls Sie nicht weiterkommen)
Finden Sie den Eintrag custom-endpoint
in Provider Info?
- Besuchen Sie die URL
https://keycloak.code-{ZAHL}.labs.corewire.de/realms/master/custom-endpoint/hello
- Verändern Sie den Endpunkt nach belieben
(Optional) Aufgabe 2 - Custom REST API für Admins
Falls Sie noch Zeit übrig haben, können Sie sich optional an folgender Aufgabe versuchen:
2.1 Custom Admin-API anlegen
- Erstellen Sie einen Admin-Endpunkt:
- Erstellen Sie eine Klasse
CustomAdminEndpointProviderFactory
, dieAdminRealmResourceProviderFactory
implementiert - Erstellen Sie eine Klasse
CustomAdminEndpointProvider
, dieAdminRealmResourceProvider
impementiert - Registrieren sie die ProviderFactory
- Erstellen Sie eine Klasse
2.2 Custom Admin-API testen
- Die Admin API ist nicht dirkt über den Browser erreichbar.
- Schauen Sie, ob der Provider in der Provider-Info aufgelistet ist.
- Testen Sie ihre API mit:
# Access Token holen:
TOKEN=$(curl -X POST "https://keycloak.code-{ZAHL}.labs.corewire.de/realms/master/protocol/openid-connect/token" -H "Content-Type: application/x-www-form-urlencoded" -d "client_id=admin-cli" -d "username=<user>" -d "password=<pass>" -d "grant_type=password" | jq -r ".access_token")
# Api testen:
curl -X GET "https://keycloak.code-{ZAHL}.labs.corewire.de/admin/realms/master/<provider-id>/<endpoint>" -H "Authorization: Bearer $TOKEN"