La sicurezza dell’applicazione va ben oltre il semplice permesso o negazione di accesso a livello superficiale. Come sviluppatore, devi implementare l'autorizzazione granulare
(FGA) per gestire i permessi a un livello più dettagliato e granulare.
FGA ti consente di impostare controlli di accesso dettagliati che specificano chi può fare cosa e sotto quali condizioni.
In questo tutorial, imparerai come implementare l'autorizzazione granulare
in Java e Spring Boot usando Permit.io.
Ecco il codice sorgente (ricorda di darglielo un like ⭐).
Spero che ti sia piaciuto il mio precedente blog sulla costruzione di un’applicazione video conferenza personalizzata con Stream e Next.js. questi blog riflettono il mio percorso nella creazione di DevTools Academy, una piattaforma progettata per aiutare i developer a scoprire strumenti developer incredibili.
Questo tutorial è un altro sforzo per presentarti a un fantastico strumento developer che ho scoperto di recente.
Indice:
Cos’è Permit?
Permit.io è una soluzione full stack, ready to use, a livello di applicazione, che consente di implementare in pochi minuti una piattaforma
sicura
,flessibile
, per ilcontrollo dell'autorizzazione
, cosicché puoi concentrarti sul ciò che è più importante.
Prerequisiti
Per comprendere appieno il tutorial, è necessario avere un’introduzione base alle Java
e Spring Boot
. Avrai anche bisogno di:
-
Permit.io: Uno strumento di sviluppo che semplifica l’implementazione dell’FGA.
-
Spring Boot Starter Web: Fornisce componenti essenziali per la costruzione di applicazioni web, inclusi API RESTful.
-
Gradle: Un strumento di compilazione per la gestione delle dipendenze.
-
JDK 11 o successiva: La versione del Java Development Kit richiesta per compilare e eseguire la tua app Spring Boot.
-
Postman o cURL: Strumenti per testare i tuoi endpoint
API
.
Cosa è l’autorizzazione fine-graffa?
L’autorizzazione fine-graffa offre un controllo d’accesso ai risorse determinando chi può accedervi, quanto e sotto quali condizioni specifiche.
Contrariamente all’autorizzazione a grana coarse (che gestisce l’accesso basandosi su categorie come ruoli utenti
come “amministratore
” o “utente
“), l’autorizzazione a grana fine offre la flessibilità per definire l’accesso a un livello granulare, per risorse o azioni specifiche e persino attributi.
In Autorizzazione a Grana Fine
esistono 3 tipi di modelli di politica per la gestione dell’autorizzazione; Controlo di Accesso Basato su Ruoli (RBAC), Controlo di Accesso Basato su Attributi (ABAC), e Controlo di Accesso Basato sulle Relazioni (ReBAC).
Consentiamoci di osservare ognuno di questi approcchi e vedere come li puoi implementare nella tua applicazione.
Controlo di Accesso Basato su Ruoli (RBAC)
RBAC è un approcio alla sicurezza che gestisce l’accesso ai risorse in base ai ruoli degli utenti all’interno di un’organizzazione. Questo modello semplifica i permessi organizzando gli utenti in ruoli e gestendo il controllo dell’accesso in base a questi ruoli definiti.
Concetti Chiave in RBAC:
Utenti: Persone che utilizzano il sistema come dipendenti o clienti.
Ruoli: Un insieme di permessi o privilegi di accesso assegnati a un gruppo di utenti in base alle loro responsabilità o compiti come amministratore, responsabile o cliente.
Permessi: I diritti concessi agli utenti per interagire con le risorse, come leggere, scrivere o eliminare.
Controlo di Accesso Basato su Attributi (ABAC)
ABAC è un modello di controllo degli accessi versatile e adattivo che decide chi può o non può accedere alle risorse in base agli attributi, come i dettagli dell’utente. Il modello ABAC consente di definire autorizzazioni dettagliate basate sugli attributi dell’utente.
Concetti chiave in ABAC:
Attributi: Caratteristiche o proprietà utilizzate per prendere decisioni sul controllo degli accessi. Gli attributi sono tipicamente categorizzati in:
-
Attributi dell’utente: Informazioni sull’utente (ad esempio, ruolo, dipartimento, titolo di lavoro, età e così via).
-
Attributi della risorsa: Caratteristiche della risorsa (ad esempio, tipo di file, livello di classificazione dei dati, data di creazione, proprietario).
-
Attributi dell’azione: L’azione che l’utente sta cercando di eseguire (ad esempio, leggere, scrivere, eliminare, approvare).
-
Attributi ambientali: Informazioni contestuali sulla richiesta di accesso (ad esempio, ora del giorno, posizione, tipo di dispositivo, indirizzo IP).
Controllo d’Accesso Basato sulle Relazioni (ReBAC)
ReBAC è un sistema di controllo d’accesso che concede permessi per accedere alle risorse in base alle relazioni tra le entità all’interno del sistema. L’approcchio enfatizza la definizione e la gestione del controllo d’accesso tramite la mappatura delle relazioni tra utenti e risorse e altre entità, come organizzazioni o gruppi.
Concezioni Chiave di ReBAC:
Entità: Utenti, risorse (come file e documenti) e altre entità, come gruppi o unità organizzative.
Relazioni: Le connessioni che specificano la relazione tra due entità. Ad esempio, un utente può essere il “proprietario” di un documento o un “membro” di un team.
Politiche: Regole che usano le relazioni per determinare i diritti d’accesso. Un utente può accedere a una risorsa o eseguire un’azione su di essa se ha una particolare relazione con essa.
Come Implementare un Autorizzazione Granulare
Ora che hai una base di conoscenza di RBAC
, ABAC
e ReBAC
, vediamo come possiamo implementare questi modelli in una applicazione e-commerce.
Implementazione del Controllo d’Accesso Basato sui Ruoli
Step 1: Vai a Permit.io, crea un account e il tuo spazio di lavoro.
Per default, dovreste vedere un progetto che include due ambienti: Sviluppo
e Produzione
.
Step 2: Crea una risorsa denominata Prodotti. Per creare la risorsa, apri la scheda Politiche nella barra laterale sinistra e poi apri la scheda Risorse in alto. Successivamente, clicca sul pulsante Crea una Risorsa e crea una risorsa chiamata Prodotti con le azioni read
, create
, update
, e delete
.
Step 3: Crea un’altra risorsa chiamata Recensioni con le azioni read
, create
, update
, e delete
.
Step 4: Apri la scheda Editor Politiche. Vedere che sono state create 3 ruoli chiamati admin
, editor
, e viewer
.
-
Il ruolo admin ha il permesso di
create
,delete
,read
, oupdate
un prodotto o una recensione. -
Il ruolo
editor
ha il permesso dicreate
,read
, oupdate
unprodotto
o unarecensione
ma non didelete
alcuno. -
Ruolo
viewer
ha il permesso dicreare
eleggere
un prodotto o unarecensione
, ma non dieliminare
oaggiornare
alcuno.
Implementazione del Controllo di Accesso Basato su Attributi
Passo 1: Apri la scheda Risorse, poi clicca il pulsante Aggiungi Attributi
-
Aggiungi un attributo chiamato fornitore
-
Aggiungi un attributo chiamato il cliente
Passo 2: Apri la scheda Regole ABAC, poi crea un nuovo Set di Risorse ABAC chiamato Prodotti Propri che dipende dalla risorsa Prodotti. Dopo di ciò, aggiungi una condizione che concede i permessi solo all’utente che ha creato un prodotto in base all’attributo fornitore.
Passo 3: Crea un altro Set di Risorse ABAC chiamato Recensioni Proprie che dipende dalla risorsa Recensioni.
Implementazione del Controllo di Accesso Basato sulle Relazioni
Passo 1: Apri la scheda Risorse e modifica la risorsa Prodotti. Aggiungi il ruolo fornitore
nella sezione Opzioni ReBAC. Poi imposta i prodotti come genitori delle recensioni nella sezione relazioni.
Passo 2: Modifica il risorsa Recensioni aggiungendo il ruolo cliente nelle opzioni ReBAC
, come mostrato qui sotto:
Passo 3: Vai alla scheda Policy
Editor
e aggiungi:
-
ruolo
venditore
permesso di aggiornare e eliminare i propri prodotti. -
ruolo
cliente
permesso di aggiornare e eliminare le proprie recensioni sui prodotti.
Come implementare FGA in Java e SpringBoot
Adesso che abbiamo definito le politiche RBAC
, ABAC
e ReBAC
nella interfaccia web Permit.io, impariamo come enforcerle in un’applicazione di gestione del sistema di e-commerce utilizzando l’API Permit.io.
Arriverà un sacco di codice, quindi assicurati di leggere le estese note che ho lasciato in ogni blocco di codice. queste vi aiuteranno a comprendere appieno cosa sta succedendo in questo codice.
Passo 1: Configurazione dell’applicazione e-commerce
Per configurare l’applicazione e-commerce e git clone il codice sorgente.
git clone https://github.com/tyaga001/java-spring-fine-grained-auth.git
Installazione del pacchetto SDK Permit
Per installare il pacchetto SDK Permit, aggiungi l’SDK sotto il blocco dipendenze nel file build.graddle
.
## 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'
// Aggiungi questa riga per installare il SDK Java Permit.io nel tuo progetto
implementation 'io.permit:permit-sdk-java:2.0.0'
}
Inizializzazione dell’SDK Permit
Puoi inizializzare il client SDK
Permit usando il codice qui sotto:
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 // Marca questa classe come una classe di configurazione per Spring IoC
public class PermitClientConfig {
@Value("${permit.api-key}") // Inject chiave API Permit da proprietà dell'applicazione
private String apiKey;
@Value("${permit.pdp-url}") // Inject URL PDP (Policy Decision Point) Permit da proprietà dell'applicazione
private String pdpUrl;
/**
* Crea una istanza del client Permit con configurazione personalizzata
* @return Istanza del client Permit
*/
@Bean
public Permit permit() {
return new Permit(
new PermitConfig.Builder(apiKey) // Inizializza PermitConfig con chiave API
.withPdpAddress(pdpUrl) // Imposta l'indirizzo del PDP
.withDebugMode(true) // Abilita la modalità debug per log di dettaglio
.build() // Costruisci l'oggetto PermitConfig
);
}
}
Sincronizzazione Utenti con l’SDK
Per iniziare ad applicare i permessi, devi prima sincronizzare un utente con Permit, e poi assegnargli un ruolo.
Nel codice qui sotto, la classe UserService fornisce metodi per il login utente, l’iscrizione, l’assegnazione del ruolo e l’autorizzazione, con il handling di eccezioni per eventuali errori durante l’interazione con l’API Permit.
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 // Marca questa classe come servizio Spring, rendendola un candidato per lo scansione componenti
public class UserService {
private final Permit permit;
// Iniezione costruttore per il SDK Permit
public UserService(Permit permit) {
this.permit = permit;
}
/**
* Simula il login utente creando e restituendo un oggetto utente Permit.
*
* @param key Chiave unica utente
* @return Oggetto utente
*/
public Object login(String key) {
return new User.Builder(key).build();
}
/**
* Gestisce l'iscrizione utente creando e sincronizzando un nuovo utente Permit.
*
* @param key Chiave unica utente
* @return Creato e sincronizzato Oggetto utente
*/
public User signup(String key) {
var user = new User.Builder(key).build();
try {
permit.api.users.sync(user); // Sincronizza il nuovo utente con il servizio Permit
} catch (PermitContextError | PermitApiError | IOException e) {
throw new RuntimeException("Failed to create user", e); // Gestisce eccezioni durante la creazione utente
}
return user;
}
/**
* Assegna un ruolo all'utente all'interno dell'ambiente "default".
*
* @param user Oggetto utente da assegnare il ruolo
* @param role Ruolo da assegnare
*/
public void assignRole(User user, String role) {
try {
permit.api.users.assignRole(user.getKey(), role, "default"); // Assegna ruolo nell'ambiente "default"
} catch (PermitApiError | PermitContextError | IOException e) {
throw new RuntimeException("Failed to assign role to user", e); // Gestisce eccezioni durante l'assegnazione ruoli
}
}
/**
* Verifica se l'utente è autorizzato a eseguire una specifica azione su un risorsa.
*
* @param user Oggetto utente richiedente autorizzazione
* @param action Azione da autorizzare
* @param resource Risorsa sulla quale verrà eseguita l'azione
* @throws UnauthorizedException se l'utente non è loggato
* @throws ForbiddenAccessException se l'utente è negato l'accesso
*/
public void authorize(User user, String action, Resource resource) {
if (user == null) {
throw new UnauthorizedException("Not logged in"); // Lancia eccezione se l'utente non è loggato
}
try {
var permitted = permit.check(user, action, resource); // Effettua il controllo autorizzazione
if (!permitted) {
throw new ForbiddenAccessException("Access denied"); // Lancia eccezione se l'accesso è negato
}
} catch (PermitApiError | IOException e) {
throw new RuntimeException("Failed to authorize user", e); // Gestisce eccezioni durante l'autorizzazione
}
}
}
Nel codice seguente, la classe UserController espone endpoint REST API per l’iscrizione utente e l’assegnazione di ruoli. Interagisce con la classe UserService per gestire il business logico utente e fornisce risposte HTTP appropriate.
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 // Indica che questa classe gestisce richieste HTTP e restituisce risposte JSON
@RequestMapping("/api/users") // Percorso base URL per tutte le operazioni relative agli utenti
public class UserController {
private final UserService userService;
// Iniezione di costruttore di UserService, contenente il business logico per le operazioni utente
public UserController(UserService userService) {
this.userService = userService;
}
/**
* Gestisce le richieste di iscrizione utente.
* Endpoint: POST /api/users/signup
*
* @param key Chiave univoca per il nuovo utente
* @return Oggetto Utente creato
*/
@PostMapping("/signup")
public User signup(@RequestBody String key) {
return userService.signup(key); // Chiama il metodo signup in UserService per creare un nuovo utente
}
/**
* Gestisce l'assegnazione di un ruolo all'utente loggato.
* Endpoint: POST /api/users/assign-role
*
* @param request Richiesta HTTP, usata per recuperare l'utente corrente
* @param role Ruolo da assegnare all'utente corrente
*/
@PostMapping("/assign-role")
public void assignRole(HttpServletRequest request, @RequestBody String role) {
// Recupera l'utente corrente dai dati della richiesta
User currentUser = (User) request.getAttribute("user");
// Lancia un'eccezione se l'utente non è loggato
if (currentUser == null) {
throw new UnauthorizedException("Not logged in");
}
// Assegna il ruolo specificato all'utente corrente
userService.assignRole(currentUser, role);
}
}
Creazione di un Punto di Enforceamento di Politiche di RBAC, ABAC e ReBAC
Nel codice seguente, la classe ProductService gestisce le operazioni CRUD per i prodotti e le recensioni, affrontando i permessi e i ruoli tramite l’API Permit.
Ogni operazione include controlli di autorizzazione
utente, con la gestione appropriata di eccezioni per gli errori dell’API Permit e per i casi di risorse non trovate.
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 // Segna questa classe come un servizio Spring
public class ProductService {
private final List<Product> products = new ArrayList<>(); // Lista in memoria per memorizzare i prodotti
private final AtomicInteger productIdCounter = new AtomicInteger(); // Contatore per generare ID unici per i prodotti
private final AtomicInteger reviewIdCounter = new AtomicInteger(); // Contatore per generare ID unici per le recensioni
// Builder per istanze di risorse Permit (prodotto e recensione)
private final Resource.Builder productResourceBuilder = new Resource.Builder("product");
private final Resource.Builder reviewResourceBuilder = new Resource.Builder("review");
private final UserService userService; // Servizio per gestire le operazioni relative agli utenti
private final Permit permit; // Istanza SDK di Permit per gestire l'autorizzazione e la gestione delle risorse
// Costruttore per iniettare le dipendenze
public ProductService(UserService userService, Permit permit) {
this.userService = userService;
this.permit = permit;
}
// Metodo per autorizzare un utente per una determinata azione su una risorsa
private void authorize(User user, String action, Resource resource) {
userService.authorize(user, action, resource);
}
// Autorizza un utente a eseguire un'azione su un prodotto specifico
private void authorize(User user, String action, Product product) {
var attributes = new HashMap<String, Object>();
attributes.put("vendor", product.getVendor()); // Aggiunge l'attributo del venditore al prodotto
userService.authorize(user, action, productResourceBuilder.withKey(product.getId().toString()).withAttributes(attributes).build());
}
// Autorizza un utente a eseguire un'azione su una recensione specifica
private void authorize(User user, String action, Review review) {
var attributes = new HashMap<String, Object>();
attributes.put("customer", review.getCustomer()); // Aggiunge l'attributo del cliente alla recensione
userService.authorize(user, action, reviewResourceBuilder.withKey(review.getId().toString()).withAttributes(attributes).build());
}
// Recupera un prodotto tramite il suo ID, genera un'eccezione se non trovato
private Product getProductById(int id) {
return products.stream().filter(product -> product.getId().equals(id))
.findFirst().orElseThrow(() -> new ResourceNotFoundException("Product with id " + id + " not found"));
}
// Recupera tutti i prodotti, verifica se l'utente è autorizzato a "leggere" i prodotti
public List<Product> getAllProducts(User user) {
authorize(user, "read", productResourceBuilder.build()); // L'utente deve avere il permesso di "lettura"
return new ArrayList<>(products); // Restituisce una copia della lista dei prodotti
}
// Recupera un prodotto tramite il suo ID, verifica se l'utente è autorizzato a "leggere" il prodotto
public Product getProduct(User user, int id) {
authorize(user, "read", productResourceBuilder.build());
return getProductById(id);
}
// Aggiunge un nuovo prodotto, autorizza l'utente e crea istanze di risorse e assegnazioni di ruoli in Permit
public Product addProduct(User user, String content) {
authorize(user, "create", productResourceBuilder.build()); // Verifica se l'utente può creare un prodotto
Product product = new Product(productIdCounter.incrementAndGet(), user.getKey(), content); // Crea nuovo prodotto
try {
// Crea istanza di risorsa in Permit e assegna il ruolo di "venditore" all'utente per questo prodotto
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); // Aggiunge il prodotto alla lista in memoria
return product;
}
// Aggiorna il contenuto di un prodotto, verifica se l'utente è autorizzato ad "aggiornare" il prodotto
public Product updateProduct(User user, int id, String content) {
Product product = getProductById(id); // Recupera il prodotto tramite il suo ID
authorize(user, "update", product); // Verifica se l'utente può aggiornare il prodotto
product.setContent(content); // Aggiorna il contenuto del prodotto
return product;
}
// Elimina un prodotto, verifica se l'utente è autorizzato ad "eliminare" il prodotto
public void deleteProduct(User user, int id) {
boolean isDeleted = products.removeIf(product -> {
if (product.getId().equals(id)) {
authorize(user, "delete", product); // Verifica se l'utente può eliminare il prodotto
return true;
} else {
return false;
}
});
if (!isDeleted) {
throw new ResourceNotFoundException("Product with id " + id + " not found");
}
try {
permit.api.resourceInstances.delete("product:" + id); // Rimuove l'istanza di risorsa del prodotto da Permit
} catch (IOException | PermitApiError | PermitContextError e) {
throw new RuntimeException(e);
}
}
// Aggiunge una recensione a un prodotto, crea un'istanza di risorsa e una relazione in Permit
public Review addReview(User user, int productId, String content) {
authorize(user, "create", reviewResourceBuilder.build()); // Verifica se l'utente può creare una recensione
Product product = getProductById(productId); // Recupera il prodotto tramite il suo ID
Review review = new Review(reviewIdCounter.incrementAndGet(), user.getKey(), content); // Crea nuova recensione
try {
// Crea un'istanza di risorsa per la recensione e imposta la relazione con il prodotto
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); // Aggiunge la recensione al prodotto
return review;
}
// Aggiorna il contenuto di una recensione, verifica se l'utente è autorizzato ad "aggiornare" la recensione
public Review updateReview(User user, int productId, int reviewId, String content) {
Product product = getProductById(productId); // Recupera il prodotto tramite il suo ID
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); // Verifica se l'utente può aggiornare la recensione
review.setContent(content); // Aggiorna il contenuto della recensione
return review;
}
// Elimina una recensione, verifica se l'utente è autorizzato ad "eliminare" la recensione
public void deleteReview(User user, int productId, int reviewId) {
Product product = getProductById(productId); // Recupera il prodotto tramite il suo ID
boolean isDeleted = product.getReviews().removeIf(review -> {
if (review.getId().equals(reviewId)) {
authorize(user, "delete", review); // Verifica se l'utente può eliminare la recensione
return true;
} else {
return false;
}
});
if (!isDeleted) {
throw new ResourceNotFoundException("Review with id " + reviewId + " not found");
}
try {
permit.api.resourceInstances.delete("review:" + reviewId); // Rimuove l'istanza di risorsa della recensione da Permit
} catch (IOException | PermitApiError | PermitContextError e) {
throw new RuntimeException(e);
}
}
}
Nel codice seguente, la classe ProductController si occupa di gestire le richieste HTTP relative ai prodotti e ai loro recensimenti. Esposizione degli endpoint per la gestione dei prodotti (come creando
, aggiornando
, eliminando
, e recuperando
) e per la gestione delle recensioni dei prodotti.
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 // Indica che questa classe è un controller REST Spring
@RequestMapping("/api/products") // URL di base per tutti gli endpoint in questo controller
public class ProductController {
private final ProductService productService; // Istanza di ProductService per gestire le operazioni relative ai prodotti
@Autowired // Autowires automaticamente il bean ProductService
public ProductController(ProductService productService) {
this.productService = productService;
}
// Richiesta GET per recuperare tutti i prodotti
@GetMapping
public List<Product> getAllProducts(HttpServletRequest request) {
User currentUser = (User) request.getAttribute("user"); // Ottiene l'utente autenticato dalla richiesta
return productService.getAllProducts(currentUser); // Chiama ProductService per ottenere tutti i prodotti per l'utente
}
// Richiesta GET per recuperare un prodotto tramite il suo ID
@GetMapping("/{id}")
public Product getProductById(HttpServletRequest request, @PathVariable("id") int id) {
User currentUser = (User) request.getAttribute("user"); // Ottiene l'utente autenticato dalla richiesta
return productService.getProduct(currentUser, id); // Chiama ProductService per ottenere il prodotto tramite ID per l'utente
}
// Richiesta POST per aggiungere un nuovo prodotto
@PostMapping
@ResponseStatus(HttpStatus.CREATED) // Imposta lo stato della risposta su 201 (Creato)
public Product addProduct(HttpServletRequest request, @RequestBody String content) {
User currentUser = (User) request.getAttribute("user"); // Ottiene l'utente autenticato dalla richiesta
return productService.addProduct(currentUser, content); // Chiama ProductService per aggiungere un nuovo prodotto
}
// Richiesta PUT per aggiornare un prodotto esistente tramite il suo ID
@PutMapping("/{id}")
public Product updateProduct(HttpServletRequest request, @PathVariable("id") int id, @RequestBody String content) {
User currentUser = (User) request.getAttribute("user"); // Ottiene l'utente autenticato dalla richiesta
return productService.updateProduct(currentUser, id, content); // Chiama ProductService per aggiornare il prodotto tramite ID
}
// Richiesta DELETE per eliminare un prodotto tramite il suo ID
@DeleteMapping("/{id}")
public String deleteProduct(HttpServletRequest request, @PathVariable("id") int id) {
User currentUser = (User) request.getAttribute("user"); // Ottiene l'utente autenticato dalla richiesta
productService.deleteProduct(currentUser, id); // Chiama ProductService per eliminare il prodotto tramite ID
return "Deleted product with id " + id; // Restituisce un messaggio di successo dopo l'eliminazione
}
// Richiesta POST per aggiungere una nuova recensione a un prodotto tramite l'ID del prodotto
@PostMapping("/{id}/review")
public Review addReview(HttpServletRequest request, @PathVariable("id") int id, @RequestBody String content) {
User currentUser = (User) request.getAttribute("user"); // Ottiene l'utente autenticato dalla richiesta
return productService.addReview(currentUser, id, content); // Chiama ProductService per aggiungere una recensione al prodotto
}
// Richiesta PUT per aggiornare una recensione esistente tramite l'ID del prodotto e della recensione
@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"); // Ottiene l'utente autenticato dalla richiesta
return productService.updateReview(currentUser, id, reviewId, content); // Chiama ProductService per aggiornare la recensione
}
// Richiesta DELETE per eliminare una recensione tramite l'ID del prodotto e della recensione
@DeleteMapping("/{id}/review/{reviewId}")
public String deleteReview(HttpServletRequest request, @PathVariable("id") int id, @PathVariable("reviewId") int reviewId) {
User currentUser = (User) request.getAttribute("user"); // Ottiene l'utente autenticato dalla richiesta
productService.deleteReview(currentUser, id, reviewId); // Chiama ProductService per eliminare la recensione
return "Deleted review with id " + reviewId + " from product " + id; // Restituisce un messaggio di successo dopo l'eliminazione
}
}
Step 2: Recupera la tua chiave API dell’ambiente
Nel pannello di controllo dell’interfaccia utente, copia la chiave API Environment
dell’ambiente attivo.
Successivamente, aggiungi la chiave API
e l’URL PDP
dell’ambiente nel file application.yaml
.
permit:
pdpUrl: 'http://localhost:7766'
apiKey: "Your Permit environment API Key"
Step 3: Distribuisci il Punto di Decisione Policy (PDP)
Il Punto di Decisione Policy (PDP) è distribuito nel tuo VPC ed è responsabile dell’evaluazione delle richieste di autorizzazione. Il PDP garantirà una latenza zero, un ottimo rendimento, una elevata disponibilità e una migliore sicurezza.
Usa il comando seguente per estrarre il contenitore Permit.io PDP da Docker Hub.
docker pull permitio/pdp-v2:latest
Poi esegui il contenitore.
docker run -it -p 7766:7000 --env PDP_DEBUG=True --env PDP_API_KEY=<YOUR_API_KEY> permitio/pdp-v2:latest
Step 4: Esecuzione dell’applicazione
Puoi eseguire l’applicazione usando il seguente comando Gradle
:
./gradlew bootRun
Visualizzazione e creazione di prodotti
Ora interagiamo con gli endpoint dell’applicazione usando REQBIN.
Prima di tutto, crea un nuovo utente usando l’endpoint /api/users/signup
.
curl -X POST "http://localhost:8080/api/users/signup" -H "Content-Type: application/json" -d 'johndoe'
Dovresti essere in grado di visualizzare l’utente nel tuo progetto Permit, sotto Directory > Tutti i tenant.
Inizialmente, l’utente non ha ruoli, quindi non può fare molto. Ad esempio, cercando di elencare i prodotti risulterà in una risposta 403 Proibito come mostrato qui sotto. Il codice errore 403 significa che l’utente non ha i permessi per accedere alla risorsa richiesta, ovvero i prodotti in questo caso. È possibile leggere di più sul differenza tra i codici di errore 401 e 403 qui.
Per consentire all’utente di visualizzare un elenco di prodotti, assegnargli il ruolo di visualizzatore usando il comando seguente:
curl -X POST "http://localhost:8080/api/users/assign-role" \
-H "Authorization: Bearer johndoe" \
-H "Content-Type: application/json" \
-d 'viewer'
Dovreste vedere che l’utente johndoe
è stato assegnato al ruolo di visualizzatore, come mostrato qui sotto:
Prendendo in considerazione che un visualizzatore può creare un prodotto, usate il comando seguente per creare un prodotto con l’utente johndoe
.
curl -X POST "http://localhost:8080/api/products" -H "Authorization: Bearer johndoe" -H "Content-Type: application/json" -d 'MacBook'
Dovreste vedere che è stato creato un nuovo prodotto con ID 1 e che l’utente johndoe
è stato aggiunto come fornitore.
Aggiunta Recensioni ai Prodotti
Per aggiungere recensioni ai prodotti, create un altro utente chiamato jane
.
curl -X POST "http://localhost:8080/api/users/signup" -H "Content-Type: application/json" -d 'jane'
Per consentire all’utente di aggiungere una recensione ai prodotti, assegnargli il ruolo di viewer
usando il comando seguente:
curl -X POST "http://localhost:8080/api/users/assign-role" \
-H "Authorization: Bearer jane" \
-H "Content-Type: application/json" \
-d 'viewer'
Poi puoi aggiungere una recensione al prodotto aggiunto da johndoe
usando il comando seguente:
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'
Congratulazioni! Hai completato il progetto per questo tutorial.
Prossimi Passi
Ora che hai imparato come implementare l’autorizzazione granulare nei tuoi applicazioni Java e Spring Boot usando Permit.io, potresti voler approfondire.
Qui ci sono alcune risorse preziose:
Prima di concludere
Spero che questo tutorial vi sia stato utile.
Ecco alcuni dei miei altri articoli blog recenti che potreste apprezzare:
Per altri tutorial su strumenti di sviluppo incredibili, assicurati di visitare il mio blog DTA.
Seguimi su Twitter per ricevere aggiornamenti in tempo reale sui miei altri progetti.
Codifica felice.
Source:
https://www.freecodecamp.org/news/fine-grained-authorization-in-java-and-springboot/