RBAC in der Praxis: Sichere Autorisierung für deine Anwendung implementieren
Hast du Schwierigkeiten, ein sicheres und skalierbares Autorisierungssystem für deine Anwendung zu implementieren? Rollenbasierte Zugangskontrolle (RBAC) ist der Industriestandard zur Verwaltung von Benutzerberechtigungen, aber die korrekte Umsetzung kann herausfordernd sein. In diesem Tutorial zeigen wir dir, wie du ein robustes RBAC-System anhand eines praxisnahen Content Management Systems (CMS) aufbaust.
Wenn du dieser Anleitung folgst, lernst du:
- ✨ Wie du fein abgestufte Berechtigungen entwirfst und implementierst, um präzise Kontrolle zu erhalten
- 🔒 Best Practices zur Organisation von Berechtigungen in sinnvolle Rollen
- 👤 Techniken für effektives Ressourcen-Eigentum
- 🚀 Möglichkeiten, dein Autorisierungssystem skalierbar und wartbar zu gestalten
- 💡 Praktische Umsetzung anhand eines realen CMS-Beispiels
Der vollständige Quellcode zu diesem Tutorial ist auf GitHub verfügbar.
Grundprinzipien von RBAC verstehen
Rollenbasierte Zugangskontrolle ist mehr als nur das Zuweisen von Berechtigungen an Benutzer. Es geht darum, einen strukturierten Ansatz für Autorisierung zu schaffen, der Sicherheit und Wartbarkeit ausbalanciert.
Mehr dazu findest du unter Was ist RBAC im Auth Wiki.
Hier sind die wichtigsten Prinzipien, denen wir bei unserer Umsetzung folgen:
Fein abgestufte Berechtigungs-Entwürfe
Fein abgestufte Berechtigungen geben dir präzise Kontrolle darüber, was Benutzer in deinem System tun dürfen. Anstatt grober Zugriffsebenen wie „admin“ oder „user“ definieren wir spezifische Aktionen, die Benutzer auf Ressourcen ausführen können. Zum Beispiel:
read:articles
– Beliebigen Artikel im System ansehencreate:articles
– Neue Artikel erstellenupdate:articles
– Bestehende Artikel bearbeitenpublish:articles
– Veröffentlichungsstatus von Artikeln ändern
Ressourcen-Eigentum und Zugangskontrolle
Ressourcen-Eigentum ist ein grundlegendes Konzept im Autorisierungsdesign unseres CMS. Während RBAC festlegt, welche Aktionen verschiedene Rollen ausführen dürfen, fügt Eigentum eine persönliche Dimension zur Zugangskontrolle hinzu:
- Autoren haben automatisch Zugriff auf Artikel, die sie erstellt haben
- Dieses natürliche Eigentumsmodell bedeutet, dass Autoren ihre eigenen Inhalte immer ansehen und bearbeiten können
- Das System prüft sowohl Rollenberechtigungen ODER Eigentum bei der Bearbeitung von Artikeln
- Zum Beispiel kann ein Autor auch ohne die Berechtigung
update:articles
seine eigenen Artikel bearbeiten - Dieses Design reduziert den Bedarf an zusätzlichen Rollenberechtigungen und erhält dennoch die Sicherheit
Dieser zweistufige Ansatz (Rollen + Eigentum) schafft ein intuitiveres und sichereres System. Publisher und Admins können weiterhin alle Inhalte über ihre Rollenberechtigungen verwalten, während Autoren die Kontrolle über ihre eigenen Werke behalten.
Sichere APIs entwerfen
Beginnen wir damit, die Kernfunktionalität unseres CMS über seine API-Endpunkte zu entwerfen:
GET /api/articles # Alle Artikel auflisten
GET /api/articles/:id # Einen bestimmten Artikel abrufen
POST /api/articles # Einen neuen Artikel erstellen
PATCH /api/articles/:id # Einen Artikel aktualisieren
DELETE /api/articles/:id # Einen Artikel löschen
PATCH /api/articles/:id/published # Veröffentlichungsstatus ändern
Zugangskontrolle für deine API umsetzen
Für jeden Endpunkt müssen wir zwei Aspekte der Zugangskontrolle berücksichtigen:
- Ressourcen-Eigentum – Gehört dem Benutzer diese Ressource?
- Rollenbasierte Berechtigungen – Erlaubt die Rolle des Benutzers diese Operation?
So handhaben wir den Zugriff für jeden Endpunkt:
Endpoint | Zugangskontroll-Logik |
---|---|
GET /api/articles | – Jeder mit list:articles -Berechtigung ODER Autoren sehen ihre eigenen Artikel |
GET /api/articles/:id | – Jeder mit read:articles -Berechtigung ODER Autor des Artikels |
POST /api/articles | – Jeder mit create:articles -Berechtigung |
PATCH /api/articles/:id | – Jeder mit update:articles -Berechtigung ODER Autor des Artikels |
DELETE /api/articles/:id | – Jeder mit delete:articles -Berechtigung ODER Autor des Artikels |
PATCH /api/articles/:id/published | – Nur Benutzer mit publish:articles -Berechtigung |
Ein skalierbares Berechtigungssystem erstellen
Basierend auf unseren API-Zugriffsanforderungen können wir diese Berechtigungen definieren:
Berechtigung | Beschreibung |
---|---|
list:articles | Die Liste aller Artikel im System ansehen |
read:articles | Den vollständigen Inhalt beliebiger Artikel lesen |
create:articles | Neue Artikel erstellen |
update:articles | Beliebige Artikel bearbeiten |
delete:articles | Beliebige Artikel löschen |
publish:articles | Veröffentlichungsstatus ändern |
Beachte, dass diese Berechtigungen nur benötigt werden, wenn du auf Ressourcen zugreifst, die dir nicht gehören. Artikelbesitzer können automatisch:
- Ihre eigenen Artikel ansehen (kein
read:articles
nötig) - Ihre eigenen Artikel bearbeiten (kein
update:articles
nötig) - Ihre eigenen Artikel löschen (kein
delete:articles
nötig)
Effektive Rollen erstellen
Jetzt, da wir unsere API und Berechtigungen definiert haben, können wir Rollen erstellen, die diese Berechtigungen logisch gruppieren:
Berechtigung/Rolle | 👑 Admin | 📝 Publisher | ✍️ Autor |
---|---|---|---|
Beschreibung | Voller Systemzugriff für komplette Inhaltsverwaltung | Kann alle Artikel ansehen und Veröffentlichungen steuern | Kann neue Artikel im System erstellen |
list:articles | ✅ | ✅ | ❌ |
read:articles | ✅ | ✅ | ❌ |
create:articles | ✅ | ❌ | ✅ |
update:articles | ✅ | ❌ | ❌ |
delete:articles | ✅ | ❌ | ❌ |
publish:articles | ✅ | ✅ | ❌ |
Hinweis: Autoren haben automatisch Lese-/Bearbeitungs-/Löschrechte für ihre eigenen Artikel, unabhängig von den Rollenberechtigungen.
Jede Rolle ist mit bestimmten Verantwortlichkeiten konzipiert:
- Admin: Hat vollständige Kontrolle über das CMS, einschließlich aller Artikeloperationen
- Publisher: Konzentriert sich auf Inhaltsprüfung und Veröffentlichungsmanagement
- Autor: Spezialisiert auf Inhaltserstellung
Diese Rollenstruktur schafft eine klare Trennung der Zuständigkeiten:
- Autoren konzentrieren sich auf die Inhaltserstellung
- Publisher verwalten Inhaltsqualität und Sichtbarkeit
- Admins behalten die Kontrolle über das Gesamtsystem
RBAC in Logto konfigurieren
Bevor du beginnst, musst du ein Konto in Logto Cloud erstellen oder du kannst auch eine selbst gehostete Logto-Instanz mit der Logto OSS-Version verwenden.
Für dieses Tutorial nutzen wir jedoch Logto Cloud der Einfachheit halber.
Deine Anwendung einrichten
- Gehe in der Logto Console zu „Applications“, um eine neue React-Anwendung zu erstellen
- Anwendungsname: Content Management System
- Anwendungstyp: Traditionelle Webanwendung
- Redirect URIs: http://localhost:5173/callback
API-Ressourcen und Berechtigungen konfigurieren
- Gehe in der Logto Console zu „API Resources“, um eine neue API-Ressource zu erstellen
- API-Name: CMS API
- API-Identifier: https://api.cms.com
- Füge der API-Ressource Berechtigungen hinzu
list:articles
read:articles
create:articles
update:articles
publish:articles
delete:articles
Rollen erstellen
Gehe zu Rollen in der Logto Console, um die folgenden Rollen für das CMS zu erstellen
- Admin
- mit allen Berechtigungen
- Publisher
- mit
read:articles
,list:articles
,publish:articles
- mit
- Autor
- mit
create:articles
- mit
Rollen Benutzern zuweisen
Gehe zum Bereich „User management“ in der Logto Console, um Benutzer zu erstellen.
Im Tab „Roles“ der Benutzerdetails kannst du dem Benutzer Rollen zuweisen.
In unserem Beispiel erstellen wir 3 Benutzer mit folgenden Rollen:
- Alex: Admin
- Bob: Publisher
- Charlie: Autor
Zu Demonstrationszwecken erstellen wir diese Ressourcen und Konfigurationen über die Logto Console. In realen Projekten kannst du diese Ressourcen und Konfigurationen programmatisch mit der von Logto bereitgestellten Management API erstellen.
Dein Frontend mit Logto RBAC integrieren
Jetzt, da wir RBAC in Logto eingerichtet haben, können wir mit der Integration ins Frontend beginnen.
Folge zuerst den Logto Quick Starts, um Logto in deine Anwendung zu integrieren.
In unserem Beispiel verwenden wir React zur Demonstration.
Nachdem du Logto in deiner Anwendung eingerichtet hast, müssen wir die RBAC-Konfigurationen hinzufügen, damit Logto funktioniert.
// frontend/src/App.tsx
const logtoConfig: LogtoConfig = {
appId: LOGTO_APP_ID, // Die App-ID, die du in der Logto Console erstellt hast
endpoint: LOGTO_ENDPOINT, // Der Endpoint, den du in der Logto Console erstellt hast
resources: [API_RESOURCE], // Der API-Ressourcen-Identifier aus der Logto Console, z. B. https://api.cms.com
// Alle Scopes, die du im Frontend von der API-Ressource anfordern möchtest
scopes: [
'list:articles',
'create:articles',
'read:articles',
'update:articles',
'delete:articles',
'publish:articles',
],
};
Denke daran, dich ab- und wieder anzumelden, damit diese Änderung wirksam wird, falls du bereits angemeldet bist.
Wenn sich der Benutzer mit Logto anmeldet und ein Zugangstoken für die oben angegebenen API-Ressourcen anfordert, fügt Logto dem Zugangstoken Berechtigungen (Scopes) hinzu, die zur Rolle des Benutzers passen.
Du kannst getAccessTokenClaims
aus dem useLogto
-Hook verwenden, um die Scopes aus dem Zugangstoken zu erhalten.
// frontend/src/hooks/use-user-data.ts
import { useLogto } from '@logto/react';
import { API_RESOURCE } from '../config';
import { useState, useEffect } from 'react';
export const useUserData = () => {
const { getAccessTokenClaims } = useLogto();
const [userScopes, setUserScopes] = useState<string[]>([]);
const [userId, setUserId] = useState<string>();
useEffect(() => {
const fetchScopes = async () => {
const token = await getAccessTokenClaims(API_RESOURCE);
setUserScopes(token?.scope?.split(' ') ?? []);
setUserId(token?.sub);
};
fetchScopes();
}, [getAccessTokenClaims]);
return { userId, userScopes };
};
Und du kannst userScopes
verwenden, um zu prüfen, ob der Benutzer die Berechtigung zum Zugriff auf die Ressource hat.
// frontend/src/pages/Dashboard.tsx
const Dashboard = () => {
const { userId, userScopes } = useUserData();
// ...
return (
<div>
{/* ... */}
{(userScopes.includes('delete:articles') || article.ownerId === userId) && (
<button
onClick={() => handleDelete(article.id)}
className="text-red-600 hover:text-red-900"
>
Delete
</button>
)}
</div>
);
};
Dein Backend mit Logto RBAC integrieren
Jetzt ist es an der Zeit, Logto RBAC in dein Backend zu integrieren.
Backend-Autorisierungsmiddleware
Zuerst müssen wir eine Middleware im Backend hinzufügen, um Benutzerberechtigungen zu prüfen, zu verifizieren, ob der Benutzer angemeldet ist, und festzustellen, ob er die erforderlichen Berechtigungen für bestimmte APIs hat.
// backend/src/middleware/auth.js
const { createRemoteJWKSet, jwtVerify } = require('jose');
const getTokenFromHeader = (headers) => {
const { authorization } = headers;
const bearerTokenIdentifier = 'Bearer';
if (!authorization) {
throw new Error('Authorization header missing');
}
if (!authorization.startsWith(bearerTokenIdentifier)) {
throw new Error('Authorization token type not supported');
}
return authorization.slice(bearerTokenIdentifier.length + 1);
};
const hasScopes = (tokenScopes, requiredScopes) => {
if (!requiredScopes || requiredScopes.length === 0) {
return true;
}
const scopeSet = new Set(tokenScopes);
return requiredScopes.every((scope) => scopeSet.has(scope));
};
const verifyJwt = async (token) => {
const JWKS = createRemoteJWKSet(new URL(process.env.LOGTO_JWKS_URL));
const { payload } = await jwtVerify(token, JWKS, {
issuer: process.env.LOGTO_ISSUER,
audience: process.env.LOGTO_API_RESOURCE,
});
return payload;
};
const requireAuth = (requiredScopes = []) => {
return async (req, res, next) => {
try {
// Token extrahieren
const token = getTokenFromHeader(req.headers);
// Token verifizieren
const payload = await verifyJwt(token);
// Benutzerinfo zur Anfrage hinzufügen
req.user = {
id: payload.sub,
scopes: payload.scope?.split(' ') || [],
};
// Erforderliche Scopes prüfen
if (!hasScopes(req.user.scopes, requiredScopes)) {
throw new Error('Insufficient permissions');
}
next();
} catch (error) {
res.status(401).json({ error: 'Unauthorized' });
}
};
};
module.exports = {
requireAuth,
hasScopes,
};
Wie du siehst, prüfen wir in dieser Middleware, ob die Frontend-Anfrage ein gültiges Zugangstoken enthält und ob die Zielgruppe (audience) des Zugangstokens mit der in der Logto Console erstellten API-Ressource übereinstimmt.
Der Grund für die Überprüfung der API-Ressource ist, dass unsere API-Ressource tatsächlich die Ressourcen unseres CMS-Backends repräsentiert und alle unsere CMS-Berechtigungen mit dieser API-Ressource verknüpft sind.
Da diese API-Ressource die CMS-Ressourcen in Logto repräsentiert, fügen wir im Frontend-Code das entsprechende Zugangstoken bei API-Anfragen an das Backend hinzu:
// frontend/src/hooks/use-api.ts
export const useApi = () => {
const { getAccessToken } = useLogto();
return useMemo(
() =>
async (endpoint: string, options: RequestInit = {}) => {
try {
// Zugangstoken für die API-Ressource holen
const token = await getAccessToken(API_RESOURCE);
if (!token) {
throw new ApiRequestError('Failed to get access token');
}
const response = await fetch(`${API_BASE_URL}${endpoint}`, {
...options,
headers: {
'Content-Type': 'application/json',
// Zugangstoken zu den Request-Headern hinzufügen
Authorization: `Bearer ${token}`,
...options.headers,
},
});
// ... handle response
return await response.json();
} catch (error) {
// ... error handling
}
},
[getAccessToken]
);
};
Jetzt können wir die requireAuth
-Middleware verwenden, um unsere API-Endpunkte zu schützen.
API-Endpunkte schützen
Für APIs, die nur für Benutzer mit bestimmten Berechtigungen zugänglich sein sollen, können wir die Einschränkungen direkt in der Middleware hinzufügen. Zum Beispiel sollte die Artikel-Erstellungs-API nur für Benutzer mit der Berechtigung create:articles
zugänglich sein:
// backend/src/routes/articles.js
const { requireAuth } = require('../middleware/auth');
router.post('/articles', requireAuth(['create:articles']), async (req, res) => {
// ...
});
Für APIs, die sowohl Berechtigungen als auch Ressourcen-Eigentum prüfen müssen, können wir die Funktion hasScopes
verwenden. Zum Beispiel können Benutzer mit dem Scope list:articles
alle Artikel abrufen, während Autoren nur ihre eigenen erstellten Artikel sehen:
// backend/src/routes/articles.js
const { requireAuth, hasScopes } = require('../middleware/auth');
router.get('/articles', requireAuth(), async (req, res) => {
try {
// Wenn der Benutzer den Scope list:articles hat, alle Artikel zurückgeben
if (hasScopes(req.user.scopes, ['list:articles'])) {
const articles = await articleDB.list();
return res.json(articles);
}
// Andernfalls nur die Artikel des Benutzers zurückgeben
const articles = await articleDB.listByOwner(req.user.id);
res.json(articles);
} catch (error) {
res.status(500).json({ error: 'Failed to fetch articles' });
}
});
An diesem Punkt haben wir die RBAC-Implementierung abgeschlossen. Du kannst dir den vollständigen Quellcode ansehen, um die komplette Umsetzung zu sehen.
Die CMS RBAC-Implementierung testen
Jetzt testen wir unsere CMS RBAC-Implementierung mit den drei gerade erstellten Benutzern.
Falls du dich mit den Zugangsdaten der in „User Management“ erstellten Benutzer nicht anmelden kannst, musst du zuerst die passende Anmeldemethode aktivieren. Gehe in der Logto Console zu „Sign-in Experience“ und aktiviere deine bevorzugte Authentifizierungsmethode (wie E-Mail + Passwort oder Benutzername + Passwort).
Melden wir uns zuerst als Alex und Charles an und erstellen einige Artikel.
Da Alex die Admin-Rolle hat, kann er Artikel erstellen, löschen, aktualisieren, veröffentlichen und alle Artikel ansehen.
Charles, mit der Autor-Rolle, kann nur eigene Artikel erstellen und nur Artikel sehen, aktualisieren und löschen, die ihm gehören.
Bob, mit der Publisher-Rolle, kann alle Artikel ansehen und veröffentlichen, aber keine erstellen, aktualisieren oder löschen.
Fazit
Herzlichen Glückwunsch! Du hast gelernt, wie du ein robustes RBAC-System in deiner Anwendung implementierst.
Für komplexere Szenarien, wie den Aufbau von Multi-Tenant-Anwendungen, bietet Logto umfassende Organisationsunterstützung. Sieh dir unseren Leitfaden Multi-Tenant SaaS-Anwendung bauen: Ein vollständiger Leitfaden von Design bis Umsetzung an, um mehr über die Implementierung von organisationsweiter Zugangskontrolle zu erfahren.
Viel Spaß beim Coden! 🚀