1. Einleitung
Die Endpunkte eines Webservers sollen häufig nur für ausgewählte Nutzer zugänglich sein. In diesem Tutorial werden zwei Methoden der Zugriffskontrolle für ein Quarkus Backend gezeigt: RBAC (Role Based Access Control) und PBAC (Policy Based Access Control).
1.1. RBAC
Bei der rollenbasierten Zugriffskontrolle werden die Zugriffsrechte eines Nutzers anhand seiner Rolle innerhalb der Organisation festgelegt.
1.2. PBAC
Bei der policybasierten Zugriffskontrolle wird die Berechtigung für einen Zugriff anhand vordefinierter Richtlinien bestimmt. Es kann als eine Erweiterung der rollenbasierten Zugriffskontrolle gesehen werden, bei der die Zugriffskontrolle von der Applikation auf einen Policy Server (in diesem Fall Keycloak) verlagert wird.
2. Keycloak development Server und Datenbank
Der Keycloak Server und die Postgres Datenbank werden für die Entwicklung mithilfe von Docker ausgeführt. Für die Orchestrierung der Container wird Docker Compose verwendet. In einem Ordner compose
wird das dafür benötigte docker-compose.yaml
abgelegt. Zusätzlich wird ein Shellscript zur Initialisierung der Datenbank erstellt.
2.2. Docker Compose
services:
db:
container_name: postgres
image: postgres:17.4-alpine3.21
restart: unless-stopped
environment:
POSTGRES_USER: app
POSTGRES_PASSWORD: app
POSTGRES_MULTIPLE_DATABASES: keycloak,db
ports:
- 5432:5432
volumes:
- ./db-postgres/db:/var/lib/postgresql/data
- ./db-postgres/import:/import
- ./pg-init-scripts:/docker-entrypoint-initdb.d
keycloak:
depends_on:
- db
container_name: keycloak
image: quay.io/keycloak/keycloak:26.1
restart: no
environment:
- KEYCLOAK_ADMIN=admin
- KEYCLOAK_ADMIN_PASSWORD=password
- KC_DB=postgres
- KC_DB_URL=jdbc:postgresql://db:5432/keycloak
- KC_DB_USERNAME=app
- KC_DB_PASSWORD=app
command:
- start-dev
ports:
- 8000:8080
2.3. Postgres init script
#!/bin/bash
set -e
set -u
function create_user_and_database() {
local database=$1
echo " Creating user and database '$database'"
psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" <<-EOSQL
CREATE USER $database;
CREATE DATABASE $database;
GRANT ALL PRIVILEGES ON DATABASE $database TO $database;
EOSQL
}
if [ -n "$POSTGRES_MULTIPLE_DATABASES" ]; then
echo "Multiple database creation requested: $POSTGRES_MULTIPLE_DATABASES"
for db in $(echo $POSTGRES_MULTIPLE_DATABASES | tr ',' ' '); do
create_user_and_database $db
done
echo "Multiple databases created"
fi
2.4. Start
Um die Container zu starten, muss folgender Shellbefehl im compose
Ordner ausgeführt werden.
docker compose up
2.5. Keycloak Konfiguration
Die Keycloak Adminoberfläche ist unter http://localhost:8000
erreichbar. Auf der Anmeldeseite muss sich mit User admin
und dem Passwort password
angemeldet werden.

2.5.1. Realm erstellen
Ein Realm ist ein eigener Bereich in dem Nutzer, Rollen und vieles mehr verwaltet werden. Realms sind voneinander abgekapselt. Für dieses Demoprojekt muss ein neuer Realm erstellt werden. Das Keycloak master Realm, dient ausschließlich zur Erstellung anderer Realms und sollte nicht für andere Zwecke verwendet werden.
Für die Erstellung eines neuen Realms muss auf das Dropdown links oben geklickt werden.

Anschließend wird der Name des Realms (demo
) eingetragen und die Erstellung bestätigt.

Für alle zukünftigen Veränderungen muss der neu erstellte demo
Realm ausgewählt sein.

2.5.2. Rollen erstellen
Für dieses Beispiel müssen zwei Rollen erstellt werden: administrator
und user
. Die genauen Rechte der Rollen folgen in den jeweiligen Applikationen. Die Erstellung der Rollen erfolgt im Menüpunkt Realm roles
.

2.5.3. Nutzer erstellen
Neben den Rollen werden für die Applikation auch zwei Nutzer angelegt. Dies geschieht über den Menüpunkt Users
.

Nutzer 1
Für den ersten Nutzer John Doe
müssen folgende Werte eingetragen werden.

Nach der Erstellung des Nutzers kann dieser über den Role mapping
Tab zu den Rollen user
und admin
hinzugefügt werden.

Im gezeigten Pop-Up muss die Filterung auf Realm Rollen umgestellt werden.

Anschließend werden die gewünschten Rollen admin
und user
ausgewählt und schließlcih auf assign
geklickt.

Zuletzt muss für den Nutzer noch ein Passwort festgelegt werden.



Nutzer 2
Das Prozedere der Erstellung eines Nutzers muss für den zweiten Nutzer wiederholt werden. Die Optionen lauten:
-
Email verified:
on
-
Username:
jane
-
Email:
jane.doe@example.com
-
First Name:
Jane
-
Last Name:
Doe
-
Password:
password
-
Rollen:
user
(KEINEadmin
Rolle)



2.6. Clients erstellen
Auf dem Keycloak Server stellen Clients Applikationen dar, die Authentifizierung eines Nutzers beantragen können. Jeder Client wird durch eine eindeutige id
identifiziert. Nicht öffentlich verteilte Clients benötigen zusätzlich zur id
ein secret
, um mit dem Keycloak Server kommunizieren zu können.
Für dieses Beispiel werden zwei Clients angelegt:
-
backend
: Ein nicht öffentlich verteilter Client, der das RBAC/PBAC Quarkus Backend darstellt. -
frontend
: Ein öffentlich verteilter Client, der eine Benutzeroberfläche (z.B. Website) darstellt. Dieser Client wird in diesem Tutorial nicht implementiert. Allerdings wird der Client dazu verwendet, um einen JSON Web Token für den Test der Endpunkte zu erhalten.

2.6.1. Backend Client


Beim Backend Client wird die Client Authentication aktiviert, da es sich um einen nicht öffentlich verteilten Client handelt.
Außerdem wird die Authorization aktiviert, um feine Kontrolle über die Rechtevergabe zu erlangen.

Während der Entwicklung werden außerdem alle URIs zugelassen (*
).
2.6.2. Frontend Client

Für das Frontend wird Client authentication nicht aktiviert, da ein Frontend (z.B. Website) öffentlich verteilt wird.
Für das Frontend werden hier erneut alle URIs erlaubt. In Produktivsystemen sollte dies allerdings auf die tatsächliche URL des Frontends beschränkt werden.
3. Zugriffskontrolle mit RBAC
3.1. Quarkus Projekt erstellen
Über die Maven CLI kann ein neues Quarkus Projekt mit allen benötigten Abhängigkeiten erstellt werden:
mvn io.quarkus.platform:quarkus-maven-plugin:3.21.0:create \
-DprojectGroupId=at.htl \
-DprojectArtifactId=backend-rbac \
-Dextensions='rest-jackson,oidc,hibernate-orm-panache,jdbc-postgresql,keycloak-authorization'
cd backend-rbac
Alternativ können auch folgende Einträge im pom.xml
ergänzt werden:
pom.xml
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-rest-jackson</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-oidc</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-hibernate-orm-panache</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-arc</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-jdbc-postgresql</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-keycloak-authorization</artifactId>
</dependency>
3.2. application.properties
Für die Anbindung an die Datenbank und an den Keycloak Server müssen folgende einträge in die application.properties
Datei des Quarkus Backends eingetragen werden:
#### datasource configuration ####(1)
quarkus.datasource.db-kind=postgresql
quarkus.datasource.username=app
quarkus.datasource.password=app
quarkus.datasource.jdbc.url=jdbc:postgresql://localhost:5432/db
#### hibernate configuration ####
quarkus.hibernate-orm.database.generation=drop-and-create
(2)
quarkus.hibernate-orm.physical-naming-strategy=org.hibernate.boot.model.naming.CamelCaseToUnderscoresNamingStrategy
#### keycloak configuration ####
(3)
quarkus.oidc.auth-server-url=http://localhost:8000/realms/demo
(4)
quarkus.oidc.client-id=backend
(5)
quarkus.oidc.credentials.secret=9xCZUZAkn4XPcraKU2vIGg3qiiJ2ieCa
#### other ####
(6)
quarkus.devservices.enabled=false
1 | In diesem Beispiel wird Postgres als Datenbank verwendet. In dieser sektion müssen gegebenenfalls die Anmeldedaten und der Datenbanktyp angepasst werden. |
2 | Durch setzten der physical-naming-strategy auf CamelCaseToUnderscoresNamingStrategy werden die Attributnamen der Entitäten automatisch von camelCase zu snake_case umgewandelt, wenn die Tabellen in der Datenbank angelegt werden. |
3 | URL des Keycloak Servers. Hier wird am Ende der URL auch der gewünschte Realm angegeben. Die URL folgt dem Format: <keycloak_url>/realms/{realm} |
4 | Id des Keycloak clients. |
5 | Secret des Keycloak Clients. Das secret muss mit dem Wert aus der Keycloak Admin Oberfläche ersetzt werden.
![]() |
6 | Wird das Quarkus OIDC Plugin verwendet, startet Quarkus automatisch einen Keycloak Docker Container. Dieses Verhalten ist für dieses Beispiel unerwünscht und wird daher deaktiviert. |
3.3. Persistenzschicht
Zu Demonstrationszwecken wird für dieses Tutorial ein kleines Datenmodell erstellt. Bei bereits bestehenden Projekten kann diese Sektion problemlos übersprungen werden.
3.3.1. Entität: Vehicle
package at.htl.feature.vehicle;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
@Entity
public class Vehicle {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
public Long id;
public String make;
public String model;
public long year;
}
Um das Beispiel kompakt zu halten wurde hier auf Getter und Setter verzichtet.
3.3.2. VehicleRepository
package at.htl.feature.vehicle;
import io.quarkus.hibernate.orm.panache.PanacheRepository;
import jakarta.enterprise.context.ApplicationScoped;
@ApplicationScoped
public class VehicleRepository implements PanacheRepository<Vehicle> {
}
3.3.3. Beispieldaten
Über die import.sql
Datei können bei Start der Applikation automatisch Testdaten in die Datenbank eingefügt werden.
insert into vehicle (make, model, year) values ('Honda', 'Civic', 1990);
insert into vehicle (make, model, year) values ('Renault', 'Clio', 2001);
insert into vehicle (make, model, year) values ('BMW', 'x5', 2003);
3.4. WebAPI
In der WebAPI werden für dieses Beispiel zwei Endpunkte registriert:
-
all: liefert eine Liste aller Fahrzeuge zurück und soll nur für Administratoren zugänglich sein.
-
byId: liefert das Fahrzeug mit der im Pfad übergebenen Id zurück. Dieser Endpunkt ist nur für Nutzer zugänglich.
package at.htl.feature.vehicle;
import jakarta.annotation.security.RolesAllowed;
import jakarta.inject.Inject;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam;
import java.util.List;
@Path("/vehicle/")
public class VehicleResource {
@Inject
VehicleRepository vehicleRepository;
@GET
@RolesAllowed("admin") (1)
public List<Vehicle> all() {
return vehicleRepository.listAll();
}
@GET
@Path("/{id}")
@RolesAllowed("user") (2)
public Vehicle byId(@PathParam("id") long id) {
return vehicleRepository
.findById(id);
}
}
Über die Annotation @RolesAllowed(…)
werden die Rollen festgelegt, für die ein Endpunkt zugänglich sein soll. Soll der Endpunkt für mehrere Rollen zugänglich sein, kann der Annotation auch ein Array übergeben werden: @RolesAllowed({"user", "admin"})
3.5. Keycloak Konfiguration
Für RBAC muss keine weitere Konfiguration von Keycloak vorgenommen werden.
3.6. Test
Für den Test muss die Quarkus Applikation gestartet werden.
mvn clean quarkus:dev
Anschließend kann folgendes Shellscript ausgeführt werden:
#!/usr/bin/bash
set -e
kc_url="http://localhost:8000"
backend_url="http://localhost:8080"
client_id="frontend"
normal_user="jane"
admin_user="john"
password="password"
cmd_resp=""
function show_cmd() {
echo $ $@
cmd_resp=$(eval "$@")
}
echo Querying jwt token endpoint...
show_cmd "curl --silent $kc_url/realms/demo/.well-known/openid-configuration | jq -r '.token_endpoint'"
tok_url=$cmd_resp
echo Found token endpoint at: $tok_url
echo -e "\nRequesting access token for normal user..."
show_cmd "curl --silent -d 'client_id=$client_id' -d 'username=$admin_user' -d 'password=$password' -d 'grant_type=password' $tok_url | jq -r '.access_token'"
token=$cmd_resp
echo -e "\nRequesting all vehicles"
echo "$ curl -H 'Authorization: Bearer ...' $backend_url/vehicle/"
curl -H "Authorization: Bearer $token" $backend_url/vehicle/
echo
echo -e "\nRequesting vehicle id=1"
echo "$ curl -H 'Authorization: Bearer ...' $backend_url/vehicle/1"
curl -H "Authorization: Bearer $token" $backend_url/vehicle/1
echo
Ein erfolgreicher Output sieht folgendermaßen aus:
Querying jwt token endpoint...
$ curl --silent http://localhost:8000/realms/demo/.well-known/openid-configuration | jq -r '.token_endpoint'
Found token endpoint at: http://localhost:8000/realms/demo/protocol/openid-connect/token
Requesting access token for normal user...
$ curl --silent -d 'client_id=frontend' -d 'username=john' -d 'password=password' -d 'grant_type=password' http://localhost:8000/realms/demo/protocol/openid-connect/token | jq -r '.access_token'
Requesting all vehicles
$ curl -H 'Authorization: Bearer ...' http://localhost:8080/vehicle/
[{"id":1,"make":"Honda","model":"Civic","year":1990},{"id":2,"make":"Renault","model":"Clio","year":2001},{"id":3,"make":"BMW","model":"x5","year":2003}]
Requesting vehicle id=1
$ curl -H 'Authorization: Bearer ...' http://localhost:8080/vehicle/1
{"id":1,"make":"Honda","model":"Civic","year":1990}
Werden keine Vehicles ausgegeben, besteht ein Konfigurationsfehler.
4. Zugriffskontrolle mit PBAC
4.1. Quarkus Projekt erstellen
Über die Maven CLI kann ein neues Quarkus Projekt mit allen benötigten Abhängigkeiten erstellt werden:
mvn io.quarkus.platform:quarkus-maven-plugin:3.21.0:create \
-DprojectGroupId=at.htl \
-DprojectArtifactId=backend-policy \
-Dextensions='rest-jackson,oidc,hibernate-orm-panache,jdbc-postgresql,keycloak-authorization'
cd backend-policy
Alternativ können auch folgende Einträge im pom.xml
ergänzt werden:
pom.xml
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-rest-jackson</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-oidc</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-hibernate-orm-panache</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-arc</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-jdbc-postgresql</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-keycloak-authorization</artifactId>
</dependency>
4.2. application.properties
Ein großteil der application.properties
Date wurde bereits im Abschnitt application.properties beschrieben
#### datasource configuration ####
quarkus.datasource.db-kind = postgresql
quarkus.datasource.username = app
quarkus.datasource.password = app
quarkus.datasource.jdbc.url = jdbc:postgresql://localhost:5432/db
#### hibernate configuration ####
quarkus.hibernate-orm.database.generation=drop-and-create
quarkus.hibernate-orm.physical-naming-strategy=org.hibernate.boot.model.naming.CamelCaseToUnderscoresNamingStrategy
#### keycloak configuration ####
quarkus.oidc.auth-server-url=http://localhost:8000/realms/demo
quarkus.oidc.client-id=backend
(1)
quarkus.oidc.credentials.secret=9xCZUZAkn4XPcraKU2vIGg3qiiJ2ieCa
(2)
quarkus.keycloak.policy-enforcer.enable=true
#### other ####
quarkus.devservices.enabled=false
1 | Secret des Keycloak Clients. Das secret muss mit dem Wert aus der Keycloak Admin Oberfläche ersetzt werden.
![]() |
2 | Für die Zugriffskontrolle mit PBAC muss in der application.properties Datei eine zusätzliche Option quarkus.keycloak.policy-enforcer.enable gesetzt werden. |
4.3. Persistenzschicht
Die Elemente der Persistenzschicht können aus der Persistenzschicht Sektion des ersten Projekts übernommen werden.
4.4. WebAPI
In der WebAPI werden wie im RBAC Beispiel zwei Endpunkte angeboten:
-
all: liefert eine Liste aller Fahrzeuge zurück und soll nur für Administratoren zugänglich sein.
-
byId: liefert das Fahrzeug mit der im Pfad übergebenen Id zurück. Dieser Endpunkt ist nur für Nutzer zugänglich.
package at.htl.feature.vehicle;
import jakarta.inject.Inject;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam;
import java.util.List;
@Path("/vehicle/")
public class VehicleResource {
@Inject
VehicleRepository vehicleRepository;
@GET
public List<Vehicle> all() {
return vehicleRepository.listAll();
}
@GET
@Path("/{id}")
public Vehicle byId(@PathParam("id") long id) {
return vehicleRepository
.findById(id);
}
}
Im Vergleich zum RBAC Beispiel müssen in der Ressource keine @RolesAllowed
Annotationen hinzugefügt werden. Die Konfiguration erfolgt ausschließlich über die Keycloak Administratoroberfläche.
4.5. Keycloak Konfiguration
Um die Vehicle Ressource zu schützen, müssen am Keycloak Server einige Regeln festgelegt werden.
4.5.1. Ressourcen
Im ersten Schritt müssen die zu schützenden Ressourcen des Clients am Keycloak Server eingetragen werden.

4.5.2. Policies
Eine Policy stellt eine Regel dar, deren Auswertung zu einer Menge von Nutzern führt. Policies werden mithilfe von Permissions mit Ressourcen verbunden, um der ausgewählten Menge an Nutzern Zugriff zu ermöglichen.
Es gibt unterschiedliche Typen von Policies. Da in diesem Beispiel bereits Rollen erstellt wurden, wird hier der Typ Role
gewählt.
Only Administrators
Only Users
Für Nutzer wird dasselbe wie für Administratoren (Sektion Only Administrators) wiederholt, nur mit der Nutzerrolle.
4.6. Test
Für den Test kann erneut das in der Sektion Test beschriebene Shellscript verwendet werden. Die Policy basierte Version sollte genau dasselbe Verhalten und Ergebnis aufweisen.