Die Sicherung Ihrer Anwendung geht über das bloße Berechtigen oder Verweigern von Zugriff auf der Oberfläche hinaus. Als Entwickler müssen Sie feinere Zugriffssteuerung
(FGA) implementieren, um Rechte auf einem detaillierteren, granularen Niveau zu verwalten.
FGA ermöglicht Ihnen, detaillierte Zugriffssteuerungen einzurichten, die festlegen, wer was tun und unter welchen Bedingungen kann.
In diesem Tutorial erfahren Sie, wie Sie feinere Zugriffssteuerung
in Java und Spring Boot mit Permit.io implementieren.
Hier ist der Quellcode (denken Sie daran, ihn mit einem Stern ⭐ zu bewerten).
Ich hoffe, Sie haben meinen vorherigen Blog über das Bauen einer benutzerdefinierten Video-Konferenz-App mit Stream und Next.js genossen. Diese Blogs spiegeln meine Reise wider, während der ich DevTools Academy, eine Plattform, die Entwicklern hilft, beeindruckende Entwickler-Tools zu entdecken, erstellte.
Dieses Tutorial ist ein weiterer Versuch, Sie mit einem super hilfreichen Entwickler-Tool zu vertraut zu machen, das ich kürzlich untersucht habe.
Inhaltsverzeichnis:
-
wie man feingliedrige Autorisierung implementiert
- wie man Rollenbasierte Zugriffssteuerung implementiert
- wie man Attributbasierte Zugriffssteuerung implementiert
- wie man Beziehungsbasierte Zugriffssteuerung implementiert
Was ist Zulassung?
Permit.io ist eine vollständige Stacks, Einsteigerschaltfläche Anwendungs-Ebene Zulassungslösung, die es Ihnen ermöglicht, eine
sichere
,flexible
,Zulassung
Schicht innerhalb von Minuten zu implementieren, sodass Sie sich auf das Wesentliche konzentrieren können.
Voraussetzungen
Um diesen Leitfaden vollständig zu verstehen, müssen Sie grundlegende Kenntnisse von Java
und Spring Boot
haben. Sie werden folgendes auch benötigen:
-
Permit.io: Ein Entwicklerwerkzeug, das die Implementierung von FGA vereinfacht.
-
Spring Boot Starter Web: Stellt grundlegende Komponenten für die Erstellung von Webanwendungen bereit, einschließlich RESTful-APIs.
-
Gradle: Ein Build-Tool für die Abhängigkeitsverwaltung.
-
JDK 11 oder neuer: Die erforderliche Java Development Kit-Version, um Ihre Spring Boot-Anwendung zu kompilieren und auszuführen.
-
Postman oder cURL: Tools zur Testung Ihrer
API
-Endpunkte.
Was ist Feingestufte Autorisierung?
Feingestufte Autorisierung bietet Zugriffskontrolle auf Ressourcen an, indem festgelegt wird, wer auf sie zugreifen kann, in welchem Ausmaß und unter bestimmten Bedingungen.
Im Gegensatz zu grobgrainigen Berechtigungen (die Zugriffe auf Basis von Kategorien wie Benutzerrollen
wie z.B. „admin
“ oder „user
„) bietet feingrainige Berechtigung die Flexibilität, Zugriffe in einem feineren Maßstab festzulegen, zum Beispiel für bestimmte Ressourcen oder Aktionen und auch Attribute.
In feingrainiger Berechtigung
gibt es drei Arten von politischen Modellen zur Berechtigungsverwaltung: Rollenbasierte Zugriffskontrolle (RBAC), Attributbasierte Zugriffskontrolle (ABAC) und Beziehungsbasierte Zugriffskontrolle (ReBAC).
Lassen Sie uns einen Blick auf jede dieser Methoden werfen und erkunden, wie Sie sie in Ihrer Anwendung umsetzen können.
Rollenbasierte Zugriffskontrolle (RBAC)
RBAC ist ein Sicherheitsansatz, der den Zugriff auf Ressourcen aufgrund der Rollen von Benutzern innerhalb einer Organisation steuert. Dieses Modell vereinfacht die Berechtigungen, indem Benutzer auf Rollen aufgeteilt werden und der Zugriffskontrolle entsprechend diesen definierten Rollen geführt wird.
Kernkonzepte in RBAC:
Benutzer: Personen, die das System verwenden, wie Mitarbeiter oder Kunden.
Rollen: Ein Satz von Berechtigungen oder Zugriffsprivilegien, die einer Gruppe von Benutzern aufgrund ihrer Verantwortungen oder Aufgaben zugewiesen werden, wie z.B. Admin, Manager oder Kunde.
Berechtigungen: Die Rechte, die Benutzern für die Interaktion mit Ressourcen gewährt, wie z.B. Lesen, Schreiben oder Löschen.
Attributbasierte Zugriffskontrolle (ABAC)
ABAC ist ein vielseitiges und anpassungsfähiges Zugriffssteuerungsmodell, das bestimmt, wer Zugriff auf Ressourcen haben kann oder nicht, basierend auf Attributen wie Benutzerangaben. Das ABAC-Modell ermöglicht die Definition von feingraineden Berechtigungen auf der Basis von Benutzerattributen.
Wichtige Konzepte in ABAC:
Attribute: Merkmale oder Eigenschaften, die zur Zugriffssteuerung verwendet werden. Attribute werden typischerweise in Kategorien unterteilt:
-
Benutzerattribute: Informationen über den Benutzer (z.B. Rolle, Abteilung, Amt, Alter und so weiter).
-
Ressourcenattribute: Merkmale der Ressource (z.B. Dateityp, Datenklassifikationsstufe, Erstellungsdatum, Besitzer).
-
Aktionsattribute: Die Aktion, die der Benutzer versuchen soll, auszuführen (z.B. lesen, schreiben, löschen, genehmigen).
-
Umweltattribute: Kontextuelle Informationen über den Zugriffsanforderungszeitpunkt (z.B. Uhrzeit, Ort, Gerätetyp, IP-Adresse).
Beziehungsbasierte Zugriffskontrolle (ReBAC)
ReBAC ist ein Zugriffskontrollsystem, das Berechtigungen für den Zugriff auf Ressourcen auf der Grundlage der Beziehungen zwischen Entitäten innerhalb eines Systems gewährt. Der Ansatz konzentriert sich auf die Definition und Verwaltung des Zugriffskontrolls, indem definiert wird, wie Benutzer auf Ressourcen und andere Entitäten wie Organisationen oder Gruppen in Beziehung stehen.
Kernkonzepte von ReBAC:
Entitäten: Benutzer, Ressourcen (wie Dateien und Dokumente) und andere Entitäten, wie Gruppen oder organisatorische Einheiten.
Beziehungen: Die Verbindungen, die die Beziehung zwischen zwei Entitäten spezifizieren. Ein Benutzer könnte beispielsweise der „Besitzer“ eines Dokuments oder ein „Mitglied“ einer Team sein.
Politiken: Regeln, die Beziehungen verwenden, um Zugriffserlaubnisse zu bestimmen. Ein Benutzer kann eine Ressource oder eine Aktion darauf ausführen, wenn er zu ihr eine bestimmte Beziehung hat.
Wie man feingestimmte Zugriffsberechtigungen implementiert
Nachdem Sie eine grundlegende understanding von RBAC
, ABAC
und ReBAC
haben, sehen wir, wie wir diese Modelle in einem E-Commerce-App implementieren können.
Rollenbasierte Zugriffskontrolle implementieren
Schritt 1: Gehe zu Permit.io und erstelle ein Konto und Ihren Arbeitsbereich.
Standardmäßig sollten Sie ein Projekt sehen, das zwei Umgebungen enthält: Entwicklung
und Produktion
.
Schritt 2: Erstellen Sie eine Ressource namens Produkte. Um die Ressource zu erstellen, öffnen Sie die Richtlinien-Registerkarte auf der linken Seitenleiste und dann die Ressourcen-Registerkarte oben. Anschließend klicken Sie auf die Schaltfläche Ressource erstellen und erstellen Sie eine Ressource namens Produkte mit den Aktionen lesen
, erstellen
, aktualisieren
und löschen
.
Schritt 3: Erstellen Sie eine weitere Ressource namens Bewertungen mit den Aktionen lesen
, erstellen
, aktualisieren
und löschen
.
Schritt 4: Öffnen Sie die Richtlinien-Editor-Registerkarte. Sie sehen, dass 3 Rollen namens admin
, editor
und viewer
wurden erstellt.
-
Die Rolle admin hat die Berechtigung, ein Produkt oder eine Bewertung zu
erstellen
,löschen
,lesen
oderaktualisieren
. -
Die Rolle
editor
hat die Berechtigung, einProdukt
oder eineBewertung
zuerstellen
,lesen
oderaktualisieren
, aber nicht zulöschen
. -
Rolle
viewer
hat Berechtigung, ein Produkt oder einereview
zucreate
undread
, kann jedoch keinedelete
oderupdate
durchführen.
Attributebasierte Zugriffssteuerung implementieren
Schritt 1:Öffne den Ressourcen-Tab und klicke dann auf die Schaltfläche Attribut hinzufügen.
-
Füge ein Attribut mit dem Namen Hersteller hinzu
-
Füge ein Attribut mit dem Namen Kunde hinzu
Schritt 2:Öffne den ABAC-Regeln-Tab und erstelle ein neues ABAC-Ressourcensatz namens Eigene Produkte, der von den Produkten-Ressourcen abhängt.Danach füge eine Bedingung hinzu, die nur dem Benutzer, der ein Produkt basierend auf dem Herstellerattribut erstellt hat, Berechtigungen gewährt.
Schritt 3:Erstelle einen weiteren ABAC-Ressourcensatz namens Eigene Bewertungen, der von den Bewertungen-Ressourcen abhängt.
Relationenbasierte Zugriffssteuerung implementieren
Schritt 1:Öffne den Ressourcen-Tab und bearbeite die Produkte-Ressource. Füge in den ReBAC
-Optionenabschnitt die Rolle Hersteller
hinzu. Weise dann Produkten in der Beziehungenseinstellungen die Bewertungen als Elternobjekt zu.
Schritt 2: Bearbeiten Sie die Bewertungen-Ressource, indem Sie im Abschnitt ReBAC
die Rolle kunde
hinzufügen, wie unten gezeigt:
Schritt 3: Gehen Sie zu Register Politik
Editor
Register und fügen Sie hinzu:
-
Rolle
händler
Berechtigung, eigene Produkte zu aktualisieren und zu löschen. -
Rolle
kunde
Berechtigung, eigenen Produktbewertungen zu aktualisieren und zu löschen.
Wie implementiere ich FGA in Java und SpringBoot
Nun, da wir RBAC
, ABAC
und ReBAC
Richtlinien in der Permit.io Weboberfläche definiert haben, lernen wir, wie wir sie in einer E-Commerce-Management-System-Anwendung mit der Permit.io API durchsetzen können.
Es wird eine Menge Code kommen, also stellen Sie sicher, dass Sie den ausführlichen Kommentare durch die jeden Codeblock lesen, die ich durch die gesamte Quellcode hinzugefügt habe. Diese helfen Ihnen, besser zu verstehen, was in diesem Code passiert.
Schritt 1: Einrichten des E-Commerce-Anwendung
Einrichten der E-Commerce-Anwendung und git Klonen des Quellcodes.
git clone https://github.com/tyaga001/java-spring-fine-grained-auth.git
Installieren Sie das Permit-Paket SDK
Um das Permit-Paket SDK zu installieren, fügen Sie das SDK unter den Abhängigkeiten-Abschnitt in der Datei build.graddle
hinzu.
## Dependencies
To set up the necessary dependencies for your Spring Boot project, include the following in your `build.gradle` file:
```groovy
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.3.0'
developmentOnly 'org.springframework.boot:spring-boot-devtools'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
// Fügen Sie diesen Codezeile hinzu, um das Permit.io Java SDK in Ihrem Projekt zu installieren
implementation 'io.permit:permit-sdk-java:2.0.0'
}
Initialisierung des Permit-SDK
Du kannst den Permit SDK
-Client mit dem untenstehenden Code initialisieren:
package com.boostmytool.store.config;
import io.permit.sdk.Permit;
import io.permit.sdk.PermitConfig;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration // Markiert diese Klasse als Konfigurationsklasse für Spring IoC
public class PermitClientConfig {
@Value("${permit.api-key}") // Inject Permit API-Schlüssel aus den Anwendungseigenschaften
private String apiKey;
@Value("${permit.pdp-url}") // Inject Permit PDP (Policy Decision Point) URL aus den Anwendungseigenschaften
private String pdpUrl;
/**
* Erstellt einen Permit-Client-Bean mit benutzerdefinierten Einstellungen
* @return Permit-Client-Instanz
*/
@Bean
public Permit permit() {
return new Permit(
new PermitConfig.Builder(apiKey) // Initialisiere PermitConfig mit API-Schlüssel
.withPdpAddress(pdpUrl) // Setze die PDP-Adresse
.withDebugMode(true) // Aktiviere Debug-Modus für detaillierte Protokollierung
.build() // Erstelle das PermitConfig-Objekt
);
}
}
Benutzer mit SDK synchronisieren
Um Berechtigungen durchzusetzen, solltest du zunächst einen Benutzer bei Permit synchronisieren und dann eine Rolle zuweisen.
In dem untenstehenden Code stellt die UserService-Klasse Methoden bereit, um Benutzer zu login, registrieren, Rollen zuzuweisen und zu autorisieren, mit Exception Handling für mögliche Fehler, wenn mit dem Permit API interagiert wird.
package com.boostmytool.store.service;
import com.boostmytool.store.exception.ForbiddenAccessException;
import com.boostmytool.store.exception.UnauthorizedException;
import io.permit.sdk.Permit;
import io.permit.sdk.api.PermitApiError;
import io.permit.sdk.api.PermitContextError;
import io.permit.sdk.enforcement.Resource;
import io.permit.sdk.enforcement.User;
import org.springframework.stereotype.Service;
import java.io.IOException;
@Service // Diese Klasse als Spring-Dienst markieren, um sie als Kandidatin für Komponenten-Scanning zu machen
public class UserService {
private final Permit permit;
// Konstruktorinjektion für das Permit-SDK
public UserService(Permit permit) {
this.permit = permit;
}
/**
* Simuliert den Benutzeranmeldung durch Erstellen und Zurückgeben eines Permit-Benutzerobjekts.
*
* @param key Der eindeutige Schlüssel des Benutzers
* @return Benutzerobjekt
*/
public Object login(String key) {
return new User.Builder(key).build();
}
/**
* Verwaltet die Benutzerregistrierung durch Erstellen und Synchronisieren eines neuen Permit-Benutzers.
*
* @param key Der eindeutige Schlüssel des Benutzers
* @return Erstelltes und synchronisiertes Benutzerobjekt
*/
public User signup(String key) {
var user = new User.Builder(key).build();
try {
permit.api.users.sync(user); // Synchronisiert das neue Benutzerobjekt mit dem Permit-Dienst
} catch (PermitContextError | PermitApiError | IOException e) {
throw new RuntimeException("Failed to create user", e); // Handhabt Ausnahmen während der Benutzer Erstellung
}
return user;
}
/**
* Weist einem Benutzer innerhalb des "default" Umfeldes eine Rolle zu.
*
* @param user Das Benutzerobjekt, dem die Rolle zugewiesen werden soll
* @param role Die zugewiesene Rolle
*/
public void assignRole(User user, String role) {
try {
permit.api.users.assignRole(user.getKey(), role, "default"); // Weist Rolle im "default" Umfeld zu
} catch (PermitApiError | PermitContextError | IOException e) {
throw new RuntimeException("Failed to assign role to user", e); // Handhabt Ausnahmen während der Rollenzuweisung
}
}
/**
* Prüft, ob der Benutzer berechtigt ist, eine bestimmte Aktion auf einer Ressource durchzuführen.
*
* @param user Das Benutzerobjekt, das Autorisierung anfordert
* @param action Die zu autorisierende Aktion
* @param resource Die Ressource, auf die die Aktion durchgeführt werden soll
* @throws UnauthorizedException, falls der Benutzer nicht angemeldet ist
* @throws ForbiddenAccessException, falls der Benutzer Zugriff verweigert bekommt
*/
public void authorize(User user, String action, Resource resource) {
if (user == null) {
throw new UnauthorizedException("Not logged in"); // Wirft Ausnahme, falls der Benutzer nicht angemeldet ist
}
try {
var permitted = permit.check(user, action, resource); // Führt Autorisierungsprüfung durch
if (!permitted) {
throw new ForbiddenAccessException("Access denied"); // Wirft Ausnahme, falls Zugriff verweigert wird
}
} catch (PermitApiError | IOException e) {
throw new RuntimeException("Failed to authorize user", e); // Handhabt Ausnahmen während der Autorisierung
}
}
}
In dem untenstehenden Code verwendet die Klasse UserController REST-API-Endpunkte für die Benutzerregistrierung und die Rollenzuweisung. Sie interagiert mit der Klasse UserService, um die betriebssystemrelevanten Geschäftslogiken für Benutzer zu bearbeiten und stellt entsprechende HTTP-Antworten bereit.
package com.boostmytool.store.controllers;
import com.boostmytool.store.exception.UnauthorizedException;
import com.boostmytool.store.service.UserService;
import io.permit.sdk.enforcement.User;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController // Zeigt an, dass diese Klasse HTTP-Anfragen bearbeitet und JSON-Antworten zurückgibt
@RequestMapping("/api/users") // Basis-URL-Pfad für alle Benutzerbezogenen Operationen
public class UserController {
private final UserService userService;
// Konstruktor-Injektion von UserService, die die Geschäftslogik für Benutzeroperationen enthält
public UserController(UserService userService) {
this.userService = userService;
}
/**
* Handhabt Anfragen zur Benutzerregistrierung.
* Endpunkt: POST /api/users/signup
*
* @param key Unikaler Schlüssel für den neuen Benutzer
* @return Erstelltes Benutzerobjekt
*/
@PostMapping("/signup")
public User signup(@RequestBody String key) {
return userService.signup(key); // ruft die signup-Methode in UserService auf, um einen neuen Benutzer zu erstellen
}
/**
* Handhabt die Zuweisung einer Rolle zum angemeldeten Benutzer.
* Endpunkt: POST /api/users/assign-role
*
* @param request HTTP-Anfrage, die den aktuellen Benutzer ausliest
* @param role Rolle, die dem aktuellen Benutzer zugewiesen werden soll
*/
@PostMapping("/assign-role")
public void assignRole(HttpServletRequest request, @RequestBody String role) {
// holt den aktuellen Benutzer aus den Anfrageattributen
User currentUser = (User) request.getAttribute("user");
// wirft eine Ausnahme, wenn der Benutzer nicht angemeldet ist
if (currentUser == null) {
throw new UnauthorizedException("Not logged in");
}
// weist dem aktuellen Benutzer die angegebene Rolle zu
userService.assignRole(currentUser, role);
}
}
Erstellung eines RBAC, ABAC und ReBAC-Richtlinien-Ausführungspunktes
In dem untenstehenden Code verwaltet die Klasse ProductService CRUD-Operationen für Produkte und Rezensionen, indem sie Berechtigungen und Rollen über die Permit-API verwaltet.
Jede Operation beinhaltet Überprüfungen auf Autorisierung
des Benutzers, mit angemessener Ausnahmebehandlung für Fehler der Permit-API und für Situationen, in denen kein Resource gefunden wird.
package com.boostmytool.store.service;
import com.boostmytool.store.exception.ResourceNotFoundException;
import com.boostmytool.store.model.Product;
import com.boostmytool.store.model.Review;
import io.permit.sdk.Permit;
import io.permit.sdk.api.PermitApiError;
import io.permit.sdk.api.PermitContextError;
import io.permit.sdk.enforcement.Resource;
import io.permit.sdk.enforcement.User;
import io.permit.sdk.openapi.models.RelationshipTupleCreate;
import io.permit.sdk.openapi.models.ResourceInstanceCreate;
import io.permit.sdk.openapi.models.RoleAssignmentCreate;
import org.springframework.stereotype.Service;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
@Service // Markiert diese Klasse als Spring-Service
public class ProductService {
private final List<Product> products = new ArrayList<>();
// In-Memory-Liste zur Speicherung von Produkten
private final AtomicInteger productIdCounter = new AtomicInteger();
// Zähler zur Erstellung eindeutiger Produkt-IDs
private final AtomicInteger reviewIdCounter = new AtomicInteger();
// Zähler zur Erstellung eindeutiger Bewertungs-IDs
// Builder für Permit-Ressourceninstanzen (Produkt und Bewertung)
private final Resource.Builder productResourceBuilder = new Resource.Builder("product");
private final Resource.Builder reviewResourceBuilder = new Resource.Builder("review");
private final UserService userService;
// Service für die Verwaltung von Benutzer-bezogenen Operationen
private final Permit permit;
// Permit SDK-Instanz für die Verwaltung von Autorisierung und Ressourcen
// Konstruktor für die Injection von Abhängigkeiten
public ProductService(UserService userService, Permit permit) {
this.userService = userService;
this.permit = permit;
}
// Methode zur Autorisierung eines Benutzers für eine bestimmte Aktion auf einer Ressource
private void authorize(User user, String action, Resource resource) {
userService.authorize(user, action, resource);
}
// Autorisiert einen Benutzer, eine Aktion auf einem bestimmten Produkt auszuführen
private void authorize(User user, String action, Product product) {
var attributes = new HashMap<String, Object>();
attributes.put("vendor", product.getVendor());
// Fügt dem Produkt das Attribut "Anbieter" hinzu
userService.authorize(user, action, productResourceBuilder.withKey(product.getId().toString()).withAttributes(attributes).build());
}
// Autorisiert einen Benutzer, eine Aktion auf einer bestimmten Bewertung auszuführen
private void authorize(User user, String action, Review review) {
var attributes = new HashMap<String, Object>();
attributes.put("customer", review.getCustomer());
// Fügt der Bewertung das Attribut "Kunde" hinzu
userService.authorize(user, action, reviewResourceBuilder.withKey(review.getId().toString()).withAttributes(attributes).build());
}
// Ruft ein Produkt anhand seiner ID ab, wirft eine Ausnahme, wenn es nicht gefunden wird
private Product getProductById(int id) {
return products.stream().filter(product -> product.getId().equals(id))
.findFirst().orElseThrow(() -> new ResourceNotFoundException("Product with id " + id + " not found"));
}
// Ruft alle Produkte ab, prüft, ob der Benutzer berechtigt ist, "Produkte zu lesen"
public List<Product> getAllProducts(User user) {
authorize(user, "read", productResourceBuilder.build());
// Der Benutzer muss die "Lese"-Berechtigung haben
return new ArrayList<>(products);
// Gibt eine Kopie der Produktsliste zurück
}
// Ruft ein Produkt anhand seiner ID ab, prüft, ob der Benutzer berechtigt ist, das "Produkt zu lesen"
public Product getProduct(User user, int id) {
authorize(user, "read", productResourceBuilder.build());
return getProductById(id);
}
// Fügt ein neues Produkt hinzu, autorisiert den Benutzer und erstellt Ressourceninstanzen und Rollenzuweisungen in Permit
public Product addProduct(User user, String content) {
authorize(user, "create", productResourceBuilder.build());
// Prüft, ob der Benutzer ein Produkt erstellen kann
Product product = new Product(productIdCounter.incrementAndGet(), user.getKey(), content);
// Erstellt ein neues Produkt
try {
// Erstellt eine Ressourceninstanz in Permit und weist dem Benutzer die "Anbieter"-Rolle für dieses Produkt zu
permit.api.resourceInstances.create(new ResourceInstanceCreate(product.getId().toString(), "product").withTenant("default"));
permit.api.roleAssignments.assign(new RoleAssignmentCreate("vendor", user.getKey()).withResourceInstance("product:" + product.getId()).withTenant("default"));
} catch (IOException | PermitApiError | PermitContextError e) {
throw new RuntimeException("Failed to create resource instance or role assignment: " + e.getMessage());
}
products.add(product);
// Fügt das Produkt zur In-Memory-Liste hinzu
return product;
}
// Aktualisiert den Inhalt eines Produkts, prüft, ob der Benutzer berechtigt ist, das "Produkt zu aktualisieren"
public Product updateProduct(User user, int id, String content) {
Product product = getProductById(id);
// Ruft das Produkt anhand seiner ID ab
authorize(user, "update", product);
// Prüft, ob der Benutzer das Produkt aktualisieren kann
product.setContent(content);
// Aktualisiert den Inhalt des Produkts
return product;
}
// Löscht ein Produkt, prüft, ob der Benutzer berechtigt ist, das "Produkt zu löschen"
public void deleteProduct(User user, int id) {
boolean isDeleted = products.removeIf(product -> {
if (product.getId().equals(id)) {
authorize(user, "delete", product);
// Prüft, ob der Benutzer das Produkt löschen kann
return true;
} else {
return false;
}
});
if (!isDeleted) {
throw new ResourceNotFoundException("Product with id " + id + " not found");
}
try {
permit.api.resourceInstances.delete("product:" + id);
// Entfernt die Ressourceninstanz des Produkts aus Permit
} catch (IOException | PermitApiError | PermitContextError e) {
throw new RuntimeException(e);
}
}
// Fügt einer Bewertung zu einem Produkt hinzu, erstellt eine Ressourceninstanz und Beziehung in Permit
public Review addReview(User user, int productId, String content) {
authorize(user, "create", reviewResourceBuilder.build());
// Prüft, ob der Benutzer eine Bewertung erstellen kann
Product product = getProductById(productId);
// Ruft das Produkt anhand seiner ID ab
Review review = new Review(reviewIdCounter.incrementAndGet(), user.getKey(), content);
// Erstellt eine neue Bewertung
try {
// Erstellt eine Ressourceninstanz für die Bewertung und setzt die Beziehung zum Produkt
permit.api.resourceInstances.create(new ResourceInstanceCreate(review.getId().toString(), "review").withTenant("default"));
permit.api.relationshipTuples.create(new RelationshipTupleCreate("product:" + productId, "parent", "review:" + review.getId()));
} catch (IOException | PermitApiError | PermitContextError e) {
throw new RuntimeException(e);
}
product.addReview(review);
// Fügt die Bewertung zum Produkt hinzu
return review;
}
// Aktualisiert den Inhalt einer Bewertung, prüft, ob der Benutzer berechtigt ist, die "Bewertung zu aktualisieren"
public Review updateReview(User user, int productId, int reviewId, String content) {
Product product = getProductById(productId);
// Ruft das Produkt anhand seiner ID ab
Review review = product.getReviews().stream().filter(c -> c.getId().equals(reviewId))
.findFirst().orElseThrow(() -> new ResourceNotFoundException("Review with id " + reviewId + " not found"));
authorize(user, "update", review);
// Prüft, ob der Benutzer die Bewertung aktualisieren kann
review.setContent(content);
// Aktualisiert den Inhalt der Bewertung
return review;
}
// Löscht eine Bewertung, prüft, ob der Benutzer berechtigt ist, die "Bewertung zu löschen"
public void deleteReview(User user, int productId, int reviewId) {
Product product = getProductById(productId);
// Ruft das Produkt anhand seiner ID ab
boolean isDeleted = product.getReviews().removeIf(review -> {
if (review.getId().equals(reviewId)) {
authorize(user, "delete", review);
// Prüft, ob der Benutzer die Bewertung löschen kann
return true;
} else {
return false;
}
});
if (!isDeleted) {
throw new ResourceNotFoundException("Review with id " + reviewId + " not found");
}
try {
permit.api.resourceInstances.delete("review:" + reviewId);
// Entfernt die Ressourceninstanz der Bewertung aus Permit
} catch (IOException | PermitApiError | PermitContextError e) {
throw new RuntimeException(e);
}
}
}
In dem untenstehenden Code ist die Klasse ProductController zuständig für HTTP-Anfragen, die mit Produkten und ihren Bewertungen in Verbindung stehen. Sie bietet Endpunkte für die Verwaltung von Produkten (wie erstellen
, aktualisieren
, löschen
und abrufen
) sowie für die Verwaltung von Produktbewertungen.
package com.boostmytool.store.controllers;
import com.boostmytool.store.model.Product;
import com.boostmytool.store.model.Review;
import com.boostmytool.store.service.ProductService;
import io.permit.sdk.enforcement.User;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController // Gibt an, dass diese Klasse ein Spring REST-Controller ist
@RequestMapping("/api/products") // Basis-URL für alle Endpunkte in diesem Controller
public class ProductController {
private final ProductService productService; // ProductService-Instanz, um produktbezogene Operationen zu bearbeiten
@Autowired // Verkabelt automatisch die ProductService-Bean
public ProductController(ProductService productService) {
this.productService = productService;
}
// GET-Anfrage, um alle Produkte abzurufen
@GetMapping
public List<Product> getAllProducts(HttpServletRequest request) {
User currentUser = (User) request.getAttribute("user"); // Holt den authentifizierten Benutzer aus der Anfrage
return productService.getAllProducts(currentUser); // Ruft ProductService auf, um alle Produkte für den Benutzer abzurufen
}
// GET-Anfrage, um ein Produkt nach seiner ID abzurufen
@GetMapping("/{id}")
public Product getProductById(HttpServletRequest request, @PathVariable("id") int id) {
User currentUser = (User) request.getAttribute("user"); // Holt den authentifizierten Benutzer aus der Anfrage
return productService.getProduct(currentUser, id); // Ruft ProductService auf, um das Produkt nach ID für den Benutzer abzurufen
}
// POST-Anfrage, um ein neues Produkt hinzuzufügen
@PostMapping
@ResponseStatus(HttpStatus.CREATED) // Setzt den Antwortstatus auf 201 (Erstellt)
public Product addProduct(HttpServletRequest request, @RequestBody String content) {
User currentUser = (User) request.getAttribute("user"); // Holt den authentifizierten Benutzer aus der Anfrage
return productService.addProduct(currentUser, content); // Ruft ProductService auf, um ein neues Produkt hinzuzufügen
}
// PUT-Anfrage, um ein bestehendes Produkt nach seiner ID zu aktualisieren
@PutMapping("/{id}")
public Product updateProduct(HttpServletRequest request, @PathVariable("id") int id, @RequestBody String content) {
User currentUser = (User) request.getAttribute("user"); // Holt den authentifizierten Benutzer aus der Anfrage
return productService.updateProduct(currentUser, id, content); // Ruft ProductService auf, um das Produkt nach ID zu aktualisieren
}
// DELETE-Anfrage, um ein Produkt nach seiner ID zu löschen
@DeleteMapping("/{id}")
public String deleteProduct(HttpServletRequest request, @PathVariable("id") int id) {
User currentUser = (User) request.getAttribute("user"); // Holt den authentifizierten Benutzer aus der Anfrage
productService.deleteProduct(currentUser, id); // Ruft ProductService auf, um das Produkt nach ID zu löschen
return "Deleted product with id " + id; // Gibt eine Erfolgsmeldung nach der Löschung zurück
}
// POST-Anfrage, um eine neue Bewertung zu einem Produkt nach Produkt-ID hinzuzufügen
@PostMapping("/{id}/review")
public Review addReview(HttpServletRequest request, @PathVariable("id") int id, @RequestBody String content) {
User currentUser = (User) request.getAttribute("user"); // Holt den authentifizierten Benutzer aus der Anfrage
return productService.addReview(currentUser, id, content); // Ruft ProductService auf, um eine Bewertung zum Produkt hinzuzufügen
}
// PUT-Anfrage, um eine bestehende Bewertung nach Produkt- und Bewertungs-ID zu aktualisieren
@PutMapping("/{id}/review/{reviewId}")
public Review updateReview(HttpServletRequest request, @PathVariable("id") int id, @PathVariable("reviewId") int reviewId, @RequestBody String content) {
User currentUser = (User) request.getAttribute("user"); // Holt den authentifizierten Benutzer aus der Anfrage
return productService.updateReview(currentUser, id, reviewId, content); // Ruft ProductService auf, um die Bewertung zu aktualisieren
}
// DELETE-Anfrage, um eine Bewertung nach Produkt- und Bewertungs-ID zu löschen
@DeleteMapping("/{id}/review/{reviewId}")
public String deleteReview(HttpServletRequest request, @PathVariable("id") int id, @PathVariable("reviewId") int reviewId) {
User currentUser = (User) request.getAttribute("user"); // Holt den authentifizierten Benutzer aus der Anfrage
productService.deleteReview(currentUser, id, reviewId); // Ruft ProductService auf, um die Bewertung zu löschen
return "Deleted review with id " + reviewId + " from product " + id; // Gibt eine Erfolgsmeldung nach der Löschung zurück
}
}
Schritt 2: Holen Sie Iren Umgebungsschlüssel
Im UI-Dashboard kopieren Sie den Umgebungsschlüssel API Key
der aktiven Umgebung.
Fügen Sie anschließend den Umgebungsschlüssel API key
und die PDP URL
in der Datei application.yaml
hinzu.
permit:
pdpUrl: 'http://localhost:7766'
apiKey: "Your Permit environment API Key"
Schritt 3: Bereitstellen von Policy Decision Point (PDP)
Der Policy Decision Point (PDP) wird in Ihrer VPC bereitgestellt und ist für die Bewertung Ihrer Autorisierungsanfragen verantwortlich. Der PDP stellt eine Latenz von Null, eine großartige Leistung, hohe Verfügbarkeit und eine verbesserte Sicherheit sicher.
Verwenden Sie das untenstehende Kommando, um den Permit.io PDP-Container von Docker Hub herunterzuladen.
docker pull permitio/pdp-v2:latest
Fahren Sie dann den Container aus.
docker run -it -p 7766:7000 --env PDP_DEBUG=True --env PDP_API_KEY=<YOUR_API_KEY> permitio/pdp-v2:latest
Schritt 4: Starten der App
Sie können die Anwendung mit dem folgenden Gradle
-Befehl starten:
./gradlew bootRun
Produkte anzeigen und erstellen
Lassen Sie uns nun mit dem REQBIN-Tool mit den Anwendungsschnittstellen interagieren.
Erstellen Sie zunächst mit dem Endepunkt /api/users/signup
einen neuen Benutzer.
curl -X POST "http://localhost:8080/api/users/signup" -H "Content-Type: application/json" -d 'johndoe'
Sie sollten in Ihrem Permit-Projekt unter Verzeichnis > Alle Mieter den Benutzer sehen können.
Anfangs hat der Benutzer keine Rollen, sodass er nicht viel tun kann. Wenn Sie zum Beispiel versuchen, die Produkte aufzulisten, erhalten Sie eine 403 Forbidden-Antwort, wie unten gezeigt. Der Fehlercode 403 bedeutet, dass der Benutzer keine Berechtigung zum Zugriff auf die angeforderte Ressource, in diesem Fall Produkte, hat. Sie können mehr über den Unterschied zwischen den Fehlercodes 401 und 403 hier erfahren.
Damit der Benutzer eine Liste der Produkte anzeigen kann, weisen Sie ihm die Rolle „Viewer“ mit dem folgenden Befehl zu:
curl -X POST "http://localhost:8080/api/users/assign-role" \
-H "Authorization: Bearer johndoe" \
-H "Content-Type: application/json" \
-d 'viewer'
Sie sollten sehen, dass dem Benutzer johndoe
die Rolle „Viewer“ zugewiesen wurde, wie unten gezeigt:
Da ein Viewer ein Produkt erstellen kann, verwenden Sie den folgenden Befehl, um ein Produkt mit dem Benutzer johndoe
zu erstellen.
curl -X POST "http://localhost:8080/api/products" -H "Authorization: Bearer johndoe" -H "Content-Type: application/json" -d 'MacBook'
Sie sollten sehen, dass ein neues Produkt mit der ID 1 erstellt wurde und dass der Benutzer johndoe
als Verkäufer hinzugefügt wurde.
Hinzufügen von Rezensionen zu Produkten
Um Rezensionen zu Produkten hinzuzufügen, erstellen Sie einen anderen Benutzer namens jane
.
curl -X POST "http://localhost:8080/api/users/signup" -H "Content-Type: application/json" -d 'jane'
Damit der Benutzer eine Rezension zu Produkten hinzufügen kann, weisen Sie ihm die Rolle „Viewer“ mit dem folgenden Befehl zu:
curl -X POST "http://localhost:8080/api/users/assign-role" \
-H "Authorization: Bearer jane" \
-H "Content-Type: application/json" \
-d 'viewer'
Dann können Sie eine Rezension zu dem von johndoe
hinzugefügten Produkt mit dem folgenden Befehl hinzufügen:
curl -X POST "http://localhost:8080/api/products/1/review" -H "Authorization: Bearer jane" -H "Content-Type: application/json" -d 'The product was in good quality'
Glückwunsch! Sie haben das Projekt für dieses Tutorial abgeschlossen.
Nächste Schritte
Nachdem Sie gelernt haben, wie Sie eine feingranulare Autorisierung in Ihren Java- und Spring Boot-Anwendungen mit Permit.io implementieren, möchten Sie möglicherweise weitere Erkundungen durchführen.
Hier sind einige wertvolle Ressourcen:
Vor dem Abschluss
Ich hoffe, Ihnen hat dieser Leitfaden Insights gegeben.
Hier sind einige meiner anderen neuesten Blogbeiträge, die Sie vielleicht genießen werden:
Um mehr Tutorials über beeindruckende Entwickler Tools zu erhalten, vergewissern Sie sich, dass Sie meine Blog DTA anschauen.
Folgen Sie mir auf Twitter, um Live-Updates über meine anderen Projekte zu erhalten.
Viel Spaß beim Codieren.
Source:
https://www.freecodecamp.org/news/fine-grained-authorization-in-java-and-springboot/