La seguridad de tu aplicación supera la simple concesión o denegación de acceso a nivel superficial. Como desarrollador, necesitas implementar autorización de grano fino
(FGA) para gestionar permisos a un nivel más detallado y granular.
FGA te permite establecer controles de acceso detallados que especifican quién puede hacer qué y bajo qué condiciones.
En este tutorial, aprenderás cómo implementar autorización de grano fino
en Java y Spring Boot utilizando Permit.io.
Aquí está el código fuente (recuerda dármelo una estrella ⭐).
Espero que haya disfrutado de mi anterior blog sobre la construcción de una aplicación de video conferencia personalizada con Stream y Next.js. Estos blogs reflejan mi viaje en la creación de DevTools Academy, una plataforma diseñada para ayudar a los desarrolladores a descubrir herramientas de desarrollo impresionantes.
Este tutorial es otro esfuerzo para presentarte a una herramienta de desarrollo muy útil que exploré recientemente.
Tabla de contenidos:
¿Qué es Permitir?
Permit.io es una solución de autorización a nivel de aplicación completa, de cascada y lista para usar que le permite implementar una capa de
autorización
segura
,flexible
en solo minutos, para que pueda centrarse en lo que más importa.
Prerequisitos
Para comprender completamente el tutorial, necesita tener un conocimiento básico de Java
y Spring Boot
. También necesitará lo siguiente:
-
Permit.io: Una herramienta de desarrollo que simplifica la implementación de FGA.
-
Spring Boot Starter Web: Proporciona componentes básicos para la construcción de aplicaciones web, incluyendo API RESTful.
-
Gradle: Una herramienta de construcción para la gestión de dependencias.
-
JDK 11 o posterior: La versión del Kit de Desarrollo de Java necesaria para compilar y ejecutar su aplicación Spring Boot.
-
Postman o cURL: Herramientas para probar sus puntos finales de
API
.
¿Qué es la Autorización Finamente Granular?
La autorización finamente granular ofrece un control de acceso a los recursos determinando quién puede acceder a ellos, en qué grado y bajo condiciones específicas.
Contrariamente a la autorización de granularidad coarse (que maneja el acceso basado en categorías como roles de usuario
como “admin
” o “usuario
“), la autorización de granularidad fina le ofrece la flexibilidad para definir el acceso a un nivel muy detallado, para recursos o acciones específicos e incluso atributos.
En Autorización de Granularidad Fina
existen 3 tipos de modelos de política para la gestión de autorización; Control de Acceso Basado en Roles (RBAC), Control de Acceso Basado en Atributos (ABAC), y Control de Acceso Basado en Relaciones (ReBAC).
Vamos a echar un vistazo a cada una de estas abordadas y ver cómo puede implementarlas en su aplicación.
Control de Acceso Basado en Roles (RBAC)
RBAC es un enfoque de seguridad que controla el acceso a recursos basado en los roles de los usuarios dentro de una organización. Este modelo simplifica las permisos organizando a los usuarios en roles y gestionando el control de acceso de acuerdo con estos roles definidos.
Conceptos clave en RBAC:
Usuarios: Personas que usan el sistema, como empleados o clientes.
Roles: Un conjunto de permisos o privilegios de acceso asignado a un grupo de usuarios basado en sus responsabilidades o tareas, como admin, gerente o cliente.
Permisos: Los derechos concedidos a los usuarios para interactuar con los recursos, como leer, escribir o eliminar.
Control de Acceso Basado en Atributos (ABAC)
ABAC es un modelo de control de acceso versátil y adaptable que decide quién puede o no puede acceder a los recursos basado en atributos, como detalles del usuario. El modelo ABAC te permite definir una autorización detallada basada en atributos del usuario.
Conceptos clave en ABAC:
Atributos: Características o propiedades utilizadas para tomar decisiones de control de acceso. Los atributos generalmente se categorizan en:
-
Atributos del usuario: Información sobre el usuario (por ejemplo, rol, departamento, título laboral, edad, etc.).
-
Atributos del recurso: Características del recurso (por ejemplo, tipo de archivo, nivel de clasificación de datos, fecha de creación, propietario).
-
Atributos de acción: La acción que el usuario intenta realizar (por ejemplo, leer, escribir, eliminar, aprobar).
-
Atributos ambientales: Información contextual sobre la solicitud de acceso (por ejemplo, hora del día, ubicación, tipo de dispositivo, dirección IP).
Control de Acceso Basado en Relaciones (ReBAC)
ReBAC es un sistema de control de acceso que concede permisos para acceder a recursos basado en las relaciones entre entidades dentro de un sistema. El enfoque destaca la definición y la gestión de control de acceso mediante la representación de cómo los usuarios se relacionan con los recursos y otras entidades, como organizaciones o grupos.
Conceptos clave de ReBAC:
Entidades: Usuarios, recursos (como archivos y documentos), y otras entidades, como grupos o unidades organizativas.
Relaciones: Las conexiones que especifican la relación entre dos entidades. Un usuario podría ser el “propietario” de un documento o un “miembro” de un equipo, por ejemplo.
Políticas: Reglas que usan relaciones para determinar los derechos de acceso. Un usuario puede acceder a un recurso o ejecutar una acción en él si tiene una relación particular con él.
Cómo Implementar Autorización Finamente Granular
Ahora que tienes un entendimiento básico de RBAC
, ABAC
, y ReBAC
, veamos cómo podemos implementar estos modelos en una aplicación de comercio electrónico.
Implementando Control de Acceso Basado en Roles
Paso 1: Vaya a Permit.io, y luego cree una cuenta y su espacio de trabajo.
Por defecto, debería ver un proyecto que incluye dos entornos: Desarrollo
y Producción
.
Paso 2: Cree un recurso llamado Productos. Para crear el recurso, abra la pestaña Política en la barra lateral izquierda y luego abra la pestaña Recursos en la parte superior. Después, haga clic en el botón Crear un Recurso y cree un recurso llamado Productos con acciones leer
, crear
, actualizar
y eliminar
.
Paso 3: Cree otro recurso llamado Reviews con acciones leer
, crear
, actualizar
y eliminar
.
Paso 4: Abra la pestaña Editor de Política. Verá que se crearon 3 roles llamados admin
, editor
y visor
.
-
El rol de administrador tiene permiso para
crear
,eliminar
,leer
oactualizar
un producto o una reseña. -
El rol
editor
tiene permiso paracrear
,leer
oactualizar
unproducto
o unareseña
pero no paraeliminar
ninguna. -
El rol
viewer
tiene permiso paracrear
yleer
un producto o unareseña
pero no paraeliminar
niactualizar
ninguno.
Implementando Control de Acceso Basado en Atributos
Paso 1: Abre la pestaña Recursos, luego haz clic en el botón Agregar Atributos.
-
Agrega un atributo llamado vendedor
-
Agrega un atributo llamado cliente
Paso 2: Abre la pestaña de Reglas ABAC, luego crea un nuevo Conjunto de Recursos ABAC llamado Productos Propios que depende del recurso Productos. Después, agrega una condición que otorgue permisos solo al usuario que creó un producto basado en el atributo vendedor.
Paso 3: Crea otro Conjunto de Recursos ABAC llamado Reseñas Propias que depende del recurso Reseñas.
Implementando Control de Acceso Basado en Relaciones
Paso 1: Abre la pestaña de Recursos y edita el recurso Productos. Agrega el rol vendedor
en la sección de opciones ReBAC
. Luego establece productos como padre de reseñas en la sección de relaciones.
Paso 2: Edita el recurso de Revisión agregando el rol de cliente en la sección de opciones de ReBAC
, como se muestra abajo:
Paso 3: Vaya a la pestaña de Política
Editor
y agregue:
-
rol
vendedor
permiso para actualizar y eliminar sus propios productos. -
rol
cliente
permiso para actualizar y eliminar sus propias reseñas sobre los productos.
Cómo implementar FGA en Java y SpringBoot
Ahora que hemos definido políticas de RBAC
, ABAC
y ReBAC
en la interfaz web de Permit.io, vamos a aprender cómo aplicarlas en una aplicación de Sistema de Gestión de Comercio Electrónico utilizando la API de Permit.io.
Van a aparecer muchas líneas de código, así que asegúrate de leer las extensas comentarios que he dejado en cada bloque de código. Estos te ayudarán a comprender mejor lo que ocurre en este código.
Paso 1: Configuración de la Aplicación de Comercio Electrónico
Para configurar la aplicación de comercio electrónico y git clone el código fuente.
git clone https://github.com/tyaga001/java-spring-fine-grained-auth.git
Instalación del paquete SDK Permit
Para instalar el SDK de Permit, agregas el SDK bajo el bloque de dependencias en el archivo 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'
// Agregue esta línea para instalar el SDK de Java de Permit.io en su proyecto
implementation 'io.permit:permit-sdk-java:2.0.0'
}
Inicializando el SDK Permit
Puede inicializar el Cliente SDK
de Permit usando el código de abajo:
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 esta clase como una clase de configuración para Spring IoC
public class PermitClientConfig {
@Value("${permit.api-key}") // Inyecta la clave de API de Permit de las propiedades de la aplicación
private String apiKey;
@Value("${permit.pdp-url}") // Inyecta la URL de PDP (Policy Decision Point) de Permit de las propiedades de la aplicación
private String pdpUrl;
/**
* Crea una instancia de cliente de Permit con configuración personalizada
* @return Instancia del cliente de Permit
*/
@Bean
public Permit permit() {
return new Permit(
new PermitConfig.Builder(apiKey) // Inicializar PermitConfig con la clave de API
.withPdpAddress(pdpUrl) // Establecer la dirección del PDP
.withDebugMode(true) // Activar el modo de depuración para un registro detallado
.build() // Construir el objeto PermitConfig
);
}
}
Sincronizando Usuarios con el SDK
Para comenzar a aplicar permisos, primero debe sincronizar un usuario con Permit, y luego asignarles un rol.
En el código de abajo, la clase UserService proporciona métodos para el inicio de sesión de usuarios, registro, asignación de roles y autorización, con manejo de excepciones para posibles errores al interactuar con la API de 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 esta clase como un servicio de Spring, haciéndola una candidata para el escaneo de componentes
public class UserService {
private final Permit permit;
// Inyección de constructor para el SDK de Permit
public UserService(Permit permit) {
this.permit = permit;
}
/**
* Simula el inicio de sesión de un usuario creando y devolviendo un objeto de usuario de Permit.
*
* @param key Clave única del usuario
* @return Objeto de usuario
*/
public Object login(String key) {
return new User.Builder(key).build();
}
/**
* Manipula el registro de usuarios creando y sincronizando un nuevo usuario de Permit.
*
* @param key Clave única del usuario
* @return Usuario creado y sincronizado
*/
public User signup(String key) {
var user = new User.Builder(key).build();
try {
permit.api.users.sync(user); // Sincroniza el nuevo usuario con el servicio de Permit
} catch (PermitContextError | PermitApiError | IOException e) {
throw new RuntimeException("Failed to create user", e); // Maneja excepciones durante la creación de usuarios
}
return user;
}
/**
* Asigna un rol al usuario dentro del entorno "default".
*
* @param user Objeto de usuario al que se le asignará el rol
* @param role Rol a asignar
*/
public void assignRole(User user, String role) {
try {
permit.api.users.assignRole(user.getKey(), role, "default"); // Asigna el rol en el entorno "default"
} catch (PermitApiError | PermitContextError | IOException e) {
throw new RuntimeException("Failed to assign role to user", e); // Maneja excepciones durante la asignación de roles
}
}
/**
* Comprueba si el usuario está autorizado para realizar una acción específica sobre un recurso.
*
* @param user Objeto de usuario solicitando la autorización
* @param action Acción que se debe autorizar
* @param resource Recurso sobre el que se realizará la acción
* @throws UnauthorizedException Si el usuario no está logueado
* @throws ForbiddenAccessException Si el usuario se le niega el acceso
*/
public void authorize(User user, String action, Resource resource) {
if (user == null) {
throw new UnauthorizedException("Not logged in"); // Lanza excepción si el usuario no está logueado
}
try {
var permitted = permit.check(user, action, resource); // Realiza la comprobación de autorización
if (!permitted) {
throw new ForbiddenAccessException("Access denied"); // Lanza excepción si se niega el acceso
}
} catch (PermitApiError | IOException e) {
throw new RuntimeException("Failed to authorize user", e); // Maneja excepciones durante la autorización
}
}
}
En el código de abajo, la clase UserController expone puntos finales de API REST para el registro de usuarios y la asignación de roles. Interactúa con la clase UserService para manejar la lógica de negocio relacionada con los usuarios y proporciona respuestas HTTP apropiadas.
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 que esta clase maneja solicitudes HTTP y devuelve respuestas en formato JSON
@RequestMapping("/api/users") // Ruta de base URL para todas las operaciones relacionadas con los usuarios
public class UserController {
private final UserService userService;
// Inyección de constructor de UserService, que contiene la lógica de negocio para las operaciones de usuario
public UserController(UserService userService) {
this.userService = userService;
}
/**
* Maneja solicitudes de registro de usuarios.
* Punto final: POST /api/users/signup
*
* @param key Clave única para el nuevo usuario
* @return Objeto User creado
*/
@PostMapping("/signup")
public User signup(@RequestBody String key) {
return userService.signup(key); // Llama al método signup en UserService para crear un nuevo usuario
}
/**
* Maneja la asignación de un rol al usuario conectado.
* Punto final: POST /api/users/assign-role
*
* @param request Solicitud HTTP, utilizada para recuperar el usuario actual
* @param role Rol a asignar al usuario actual
*/
@PostMapping("/assign-role")
public void assignRole(HttpServletRequest request, @RequestBody String role) {
// Recupera el usuario actual de los atributos de la solicitud
User currentUser = (User) request.getAttribute("user");
// Lanza una excepción si el usuario no está conectado
if (currentUser == null) {
throw new UnauthorizedException("Not logged in");
}
// Asigna el rol especificado al usuario actual
userService.assignRole(currentUser, role);
}
}
Creando Punto de Aplicación de Política de RBAC, ABAC y ReBAC
En el código de abajo, la clase ProductService gestiona las operaciones CRUD para productos y reseñas, manejando permisos y roles a través de la API Permit.
Cada operación incluye chequeos de autorización
de usuario, con manejo apropiado de excepciones para errores de la API Permit y escenarios de recurso no encontrado.
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 // Marca esta clase como un servicio de Spring
public class ProductService {
private final List<Product> products = new ArrayList<>(); // Lista en memoria para almacenar productos
private final AtomicInteger productIdCounter = new AtomicInteger(); // Contador para generar identificadores únicos de productos
private final AtomicInteger reviewIdCounter = new AtomicInteger(); // Contador para generar identificadores únicos de reseñas
// Constructores para instancias de recursos de permisos (producto y reseña)
private final Resource.Builder productResourceBuilder = new Resource.Builder("product");
private final Resource.Builder reviewResourceBuilder = new Resource.Builder("review");
private final UserService userService; // Servicio para manejar operaciones relacionadas con usuarios
private final Permit permit; // Instancia de SDK de Permiso para manejar autorización y gestión de recursos
// Constructor para inyectar dependencias
public ProductService(UserService userService, Permit permit) {
this.userService = userService;
this.permit = permit;
}
// Método para autorizar a un usuario para una acción específica en un recurso
private void authorize(User user, String action, Resource resource) {
userService.authorize(user, action, resource);
}
// Autoriza a un usuario a realizar una acción en un producto específico
private void authorize(User user, String action, Product product) {
var attributes = new HashMap<String, Object>();
attributes.put("vendor", product.getVendor()); // Agrega el atributo de proveedor al producto
userService.authorize(user, action, productResourceBuilder.withKey(product.getId().toString()).withAttributes(attributes).build());
}
// Autoriza a un usuario a realizar una acción en una reseña específica
private void authorize(User user, String action, Review review) {
var attributes = new HashMap<String, Object>();
attributes.put("customer", review.getCustomer()); // Agrega el atributo de cliente a la reseña
userService.authorize(user, action, reviewResourceBuilder.withKey(review.getId().toString()).withAttributes(attributes).build());
}
// Obtiene un producto por su ID, lanza una excepción si no se encuentra
private Product getProductById(int id) {
return products.stream().filter(product -> product.getId().equals(id))
.findFirst().orElseThrow(() -> new ResourceNotFoundException("Product with id " + id + " not found"));
}
// Obtiene todos los productos, verifica si el usuario está autorizado para "leer" productos
public List<Product> getAllProducts(User user) {
authorize(user, "read", productResourceBuilder.build()); // El usuario debe tener permiso de "lectura"
return new ArrayList<>(products); // Devuelve una copia de la lista de productos
}
// Obtiene un producto por su ID, verifica si el usuario está autorizado para "leer" el producto
public Product getProduct(User user, int id) {
authorize(user, "read", productResourceBuilder.build());
return getProductById(id);
}
// Agrega un nuevo producto, autoriza al usuario y crea instancias de recursos y asignaciones de roles en Permit
public Product addProduct(User user, String content) {
authorize(user, "create", productResourceBuilder.build()); // Verifica si el usuario puede crear un producto
Product product = new Product(productIdCounter.incrementAndGet(), user.getKey(), content); // Crea un nuevo producto
try {
// Crea una instancia de recurso en Permit y asigna el rol "proveedor" al usuario para este producto
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); // Agrega el producto a la lista en memoria
return product;
}
// Actualiza el contenido de un producto, verifica si el usuario está autorizado para "actualizar" el producto
public Product updateProduct(User user, int id, String content) {
Product product = getProductById(id); // Obtiene el producto por su ID
authorize(user, "update", product); // Verifica si el usuario puede actualizar el producto
product.setContent(content); // Actualiza el contenido del producto
return product;
}
// Elimina un producto, verifica si el usuario está autorizado para "eliminar" el producto
public void deleteProduct(User user, int id) {
boolean isDeleted = products.removeIf(product -> {
if (product.getId().equals(id)) {
authorize(user, "delete", product); // Verifica si el usuario puede eliminar el producto
return true;
} else {
return false;
}
});
if (!isDeleted) {
throw new ResourceNotFoundException("Product with id " + id + " not found");
}
try {
permit.api.resourceInstances.delete("product:" + id); // Elimina la instancia de recurso del producto de Permit
} catch (IOException | PermitApiError | PermitContextError e) {
throw new RuntimeException(e);
}
}
// Agrega una reseña a un producto, crea una instancia de recurso y una relación en Permit
public Review addReview(User user, int productId, String content) {
authorize(user, "create", reviewResourceBuilder.build()); // Verifica si el usuario puede crear una reseña
Product product = getProductById(productId); // Obtiene el producto por su ID
Review review = new Review(reviewIdCounter.incrementAndGet(), user.getKey(), content); // Crea una nueva reseña
try {
// Crea una instancia de recurso para la reseña y establece la relación con el producto
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); // Agrega la reseña al producto
return review;
}
// Actualiza el contenido de una reseña, verifica si el usuario está autorizado para "actualizar" la reseña
public Review updateReview(User user, int productId, int reviewId, String content) {
Product product = getProductById(productId); // Obtiene el producto por su 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 si el usuario puede actualizar la reseña
review.setContent(content); // Actualiza el contenido de la reseña
return review;
}
// Elimina una reseña, verifica si el usuario está autorizado para "eliminar" la reseña
public void deleteReview(User user, int productId, int reviewId) {
Product product = getProductById(productId); // Obtiene el producto por su ID
boolean isDeleted = product.getReviews().removeIf(review -> {
if (review.getId().equals(reviewId)) {
authorize(user, "delete", review); // Verifica si el usuario puede eliminar la reseña
return true;
} else {
return false;
}
});
if (!isDeleted) {
throw new ResourceNotFoundException("Review with id " + reviewId + " not found");
}
try {
permit.api.resourceInstances.delete("review:" + reviewId); // Elimina la instancia de recurso de la reseña de Permit
} catch (IOException | PermitApiError | PermitContextError e) {
throw new RuntimeException(e);
}
}
}
En el código que se muestra a continuación, la clase ProductController maneja solicitudes HTTP relacionadas con productos y sus reseñas. Expose puntos finales para la gestión de productos (como creando
, actualizando
, eliminando
y recuperando
) y para la gestión de reseñas de productos.
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 que esta clase es un controlador REST de Spring
@RequestMapping("/api/products") // URL base para todos los endpoints en este controlador
public class ProductController {
private final ProductService productService; // Instancia de ProductService para manejar operaciones relacionadas con productos
@Autowired // Autoconfigura el bean de ProductService automáticamente
public ProductController(ProductService productService) {
this.productService = productService;
}
// Solicitud GET para recuperar todos los productos
@GetMapping
public List<Product> getAllProducts(HttpServletRequest request) {
User currentUser = (User) request.getAttribute("user"); // Obtiene el usuario autenticado de la solicitud
return productService.getAllProducts(currentUser); // Llama a ProductService para obtener todos los productos para el usuario
}
// Solicitud GET para recuperar un producto por su ID
@GetMapping("/{id}")
public Product getProductById(HttpServletRequest request, @PathVariable("id") int id) {
User currentUser = (User) request.getAttribute("user"); // Obtiene el usuario autenticado de la solicitud
return productService.getProduct(currentUser, id); // Llama a ProductService para obtener el producto por ID para el usuario
}
// Solicitud POST para agregar un nuevo producto
@PostMapping
@ResponseStatus(HttpStatus.CREATED) // Establece el estado de respuesta a 201 (Creado)
public Product addProduct(HttpServletRequest request, @RequestBody String content) {
User currentUser = (User) request.getAttribute("user"); // Obtiene el usuario autenticado de la solicitud
return productService.addProduct(currentUser, content); // Llama a ProductService para agregar un nuevo producto
}
// Solicitud PUT para actualizar un producto existente por su ID
@PutMapping("/{id}")
public Product updateProduct(HttpServletRequest request, @PathVariable("id") int id, @RequestBody String content) {
User currentUser = (User) request.getAttribute("user"); // Obtiene el usuario autenticado de la solicitud
return productService.updateProduct(currentUser, id, content); // Llama a ProductService para actualizar el producto por ID
}
// Solicitud DELETE para eliminar un producto por su ID
@DeleteMapping("/{id}")
public String deleteProduct(HttpServletRequest request, @PathVariable("id") int id) {
User currentUser = (User) request.getAttribute("user"); // Obtiene el usuario autenticado de la solicitud
productService.deleteProduct(currentUser, id); // Llama a ProductService para eliminar el producto por ID
return "Deleted product with id " + id; // Devuelve un mensaje de éxito después de la eliminación
}
// Solicitud POST para agregar una nueva reseña a un producto por ID de producto
@PostMapping("/{id}/review")
public Review addReview(HttpServletRequest request, @PathVariable("id") int id, @RequestBody String content) {
User currentUser = (User) request.getAttribute("user"); // Obtiene el usuario autenticado de la solicitud
return productService.addReview(currentUser, id, content); // Llama a ProductService para agregar una reseña al producto
}
// Solicitud PUT para actualizar una reseña existente por ID de producto y reseña
@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"); // Obtiene el usuario autenticado de la solicitud
return productService.updateReview(currentUser, id, reviewId, content); // Llama a ProductService para actualizar la reseña
}
// Solicitud DELETE para eliminar una reseña por ID de producto y reseña
@DeleteMapping("/{id}/review/{reviewId}")
public String deleteReview(HttpServletRequest request, @PathVariable("id") int id, @PathVariable("reviewId") int reviewId) {
User currentUser = (User) request.getAttribute("user"); // Obtiene el usuario autenticado de la solicitud
productService.deleteReview(currentUser, id, reviewId); // Llama a ProductService para eliminar la reseña
return "Deleted review with id " + reviewId + " from product " + id; // Devuelve un mensaje de éxito después de la eliminación
}
}
Paso 2: Obtén tu clave de API del entorno
En el panel de instrumentos de la interfaz de usuario, copia la API Key
del entorno activo.
A continuación, agrega la API key
del entorno y la PDP URL
en el archivo application.yaml
.
permit:
pdpUrl: 'http://localhost:7766'
apiKey: "Your Permit environment API Key"
Paso 3: Implementar Punto de Decisión de Política (PDP)
El Punto de Decisión de Política (PDP) se implementa en tu VPC y es responsable de evaluar tus solicitudes de autorización. El PDP garantizará cero latencia, excelente rendimiento, alta disponibilidad y mejor seguridad.
Use el comando siguiente para extraer el contenedor de Permit.io PDP de Docker
Hub.
docker pull permitio/pdp-v2:latest
A continuación, ejecute el contenedor.
docker run -it -p 7766:7000 --env PDP_DEBUG=True --env PDP_API_KEY=<YOUR_API_KEY> permitio/pdp-v2:latest
Paso 4: Ejecutar la aplicación
Puede ejecutar la aplicación usando el siguiente comando de Gradle
:
./gradlew bootRun
Ver y crear productos
Vamos a interactuar con los puntos finales de la aplicación usando REQBIN.
Primero, cree un nuevo usuario usando el punto final /api/users/signup
.
curl -X POST "http://localhost:8080/api/users/signup" -H "Content-Type: application/json" -d 'johndoe'
Debería ser capaz de ver al usuario en su proyecto Permit, en Directorio > Todos los inquilinos.
Inicialmente, el usuario no tiene roles, por lo que no puede hacer mucho. Por ejemplo, intentar listar los productos resultará en una respuesta 403 Prohibido, como se muestra a continuación. El código de error 403 significa que el usuario no tiene permisos para acceder a la recurso solicitado, que en este caso son los productos. Puede aprender más sobre la diferencia entre los códigos de error 401 y 403 aquí.
Para que el usuario vea una lista de productos, asígnale un rol de visualización usando el comando a continuación:
curl -X POST "http://localhost:8080/api/users/assign-role" \
-H "Authorization: Bearer johndoe" \
-H "Content-Type: application/json" \
-d 'viewer'
Debería ver que el usuario johndoe
se asignó el rol de visualización, como se muestra a continuación:
Como un visualizador puede crear un producto, use el comando a continuación para crear un producto con el usuario johndoe
.
curl -X POST "http://localhost:8080/api/products" -H "Authorization: Bearer johndoe" -H "Content-Type: application/json" -d 'MacBook'
Debería ver que se creó un nuevo producto con ID 1 y que el usuario johndoe
se agregó como proveedor.
Agregando reseñas a productos
Para agregar reseñas a productos, cree otro usuario llamado jane
.
curl -X POST "http://localhost:8080/api/users/signup" -H "Content-Type: application/json" -d 'jane'
Para que el usuario agregue una reseña a los productos, asígnale un rol de visualización
usando el comando a continuación:
curl -X POST "http://localhost:8080/api/users/assign-role" \
-H "Authorization: Bearer jane" \
-H "Content-Type: application/json" \
-d 'viewer'
A continuación, puede agregar una reseña al producto agregado por johndoe
usando el comando a continuación:
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'
¡Felicitaciones! Ha completado el proyecto de este tutorial.
Pasos siguientes
Ahora que ha aprendido cómo implementar una autorización de alta granularidad en sus aplicaciones Java y Spring Boot usando Permit.io, tal vez quiera explorar más adelante.
Aquí tienes algunos recursos valiosos:
Antes de que terminemos
Espero que haya encontrado este tutorial informativo.
Aquí tienes algunos de mis otros posts de blog recientes que podrías disfrutar:
Para más tutoriales sobre herramientas de desarrollo impresionantes, asegúrate de revisar mi blog DTA.
Sígueme en Twitter para obtener actualizaciones en vivo sobre mis otros proyectos.
¡Feliz codificación.
Source:
https://www.freecodecamp.org/news/fine-grained-authorization-in-java-and-springboot/