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:

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.

💡
Note: You need to define and test your policies in the development environment before deploying them to production.

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 oder aktualisieren.

  • Die Rolle editor hat die Berechtigung, ein Produkt oder eine Bewertung zu erstellen, lesen oder aktualisieren, aber nicht zu löschen.

  • Rolle viewer hat Berechtigung, ein Produkt oder eine review zu create und read, kann jedoch keine delete oder update 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
💡
Then open the code in your Java IDE. I used JetBrains for all my work.

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.