RBAC vs ABAC: estrategias de autorización con Spring Security

Avanzado
Spring Security
Spring Security
Actualizado: 26/04/2026

Diagrama: RBAC vs ABAC comparativa

La autorización se reduce, al final, a una decisión: este sujeto tiene permiso para esta acción sobre este recurso. Cómo se modela ese cálculo da lugar a tres familias dominantes: RBAC, ABAC y ReBAC.

Spring Security cubre las tres con su API de AuthorizationManager y permite combinarlas en la misma aplicación. Esta lección explica las diferencias y cómo aplicarlas en proyectos reales.

flowchart TD
    Q[Decisión de autorización] --> RBAC["RBAC<br>roles fijos -> permisos"]
    Q --> ABAC["ABAC<br>atributos sujeto + recurso + entorno"]
    Q --> ReBAC["ReBAC<br>relaciones entre entidades<br>tipo Google Zanzibar"]
    RBAC --> SS1["hasRole / hasAuthority"]
    ABAC --> SS2["AuthorizationManager + SpEL"]
    ReBAC --> SS3["Cliente OpenFGA / SpiceDB<br>via AuthorizationManager custom"]

RBAC (Role-Based Access Control)

El modelo más extendido. Cada usuario tiene roles, cada rol tiene permisos sobre tipos de recursos.

Empleada Ana: roles = [EDITOR]
Rol EDITOR: permisos = [leer:articulos, escribir:articulos]
@PreAuthorize("hasRole('EDITOR')")
public Articulo crearArticulo(NuevoArticuloCmd cmd) { ... }

@PreAuthorize("hasAnyRole('EDITOR', 'REVISOR')")
public List<Articulo> listarPendientes() { ... }

Ventajas: simple de explicar, fácil de auditar, encaja en LDAP y bases de datos relacionales.

Limitaciones: explosión de roles cuando el dominio crece. Es típico ver organizaciones con cientos de roles tipo editor_marketing_es, editor_marketing_es_borradores, etc. Esa proliferación es el síntoma de que RBAC se queda corto.

RBAC funciona bien cuando los permisos se asignan estáticamente a perfiles de puesto y los recursos no varían por instancia.

ABAC (Attribute-Based Access Control)

ABAC añade atributos del sujeto, del recurso y del entorno al cálculo. Una política ABAC se expresa como una expresión que combina esos atributos.

Ejemplo: "Un editor puede modificar un artículo si pertenece al mismo departamento, el artículo está en estado borrador y la hora actual está dentro del horario laboral".

@PreAuthorize("@politicas.puedeEditarArticulo(authentication, #articuloId)")
public Articulo actualizar(Long articuloId, ActualizarCmd cmd) { ... }
@Component("politicas")
public class PoliticasService {

    public boolean puedeEditarArticulo(Authentication auth, Long articuloId) {
        Usuario user = (Usuario) auth.getPrincipal();
        Articulo art = repo.findById(articuloId).orElseThrow();
        boolean mismoDepto = user.getDepartamento().equals(art.getDepartamento());
        boolean enBorrador = art.getEstado() == EstadoArticulo.BORRADOR;
        boolean horarioLaboral = LocalTime.now().isAfter(LocalTime.of(8, 0))
                              && LocalTime.now().isBefore(LocalTime.of(20, 0));
        return user.getRoles().contains("EDITOR") && mismoDepto && enBorrador && horarioLaboral;
    }
}

Ventajas: extremadamente expresivo, evita la explosión de roles.

Limitaciones: las políticas se reparten por servicios y son difíciles de auditar centralizadamente. Aparecen motores como OPA (Open Policy Agent) precisamente para externalizar estas reglas.

ReBAC (Relationship-Based Access Control)

Inspirado en el paper Google Zanzibar (2019), modela los permisos como relaciones tipo grafo entre sujetos y recursos.

documento:42#editor@usuario:ana
carpeta:proyecto-x#parent@documento:42
grupo:equipo-pagos#member@usuario:bob
documento:42#viewer@grupo:equipo-pagos

Las consultas son tipo "puede usuario:ana realizar editor sobre documento:42?". El motor resuelve recorriendo el grafo de relaciones.

Spring no incluye un motor ReBAC, pero hay implementaciones open source compatibles con Java:

  • OpenFGA (CNCF): emulación de Zanzibar con SDK Java.
  • SpiceDB (de Authzed): otro Zanzibar moderno con cliente gRPC.

Integración con OpenFGA:

@Component
public class OpenFgaAuthorizationManager implements AuthorizationManager<MethodInvocation> {

    private final OpenFgaClient fga;

    @Override
    public AuthorizationDecision check(Supplier<Authentication> authSupplier,
                                       MethodInvocation invocation) {
        Authentication auth = authSupplier.get();
        Object[] args = invocation.getArguments();
        if (args.length == 0) return new AuthorizationDecision(false);

        CheckRequest req = new CheckRequest()
            .user("user:" + auth.getName())
            .relation("editor")
            .object("documento:" + args[0]);
        boolean allowed = fga.check(req).get().getAllowed();
        return new AuthorizationDecision(allowed);
    }
}

ReBAC brilla cuando las relaciones tienen jerarquías (carpetas anidadas, equipos en organizaciones). Es el modelo que usan internamente Google Drive, GitHub o Dropbox.

Comparativa práctica

| Aspecto | RBAC | ABAC | ReBAC | |---|---|---|---| | Curva de aprendizaje | Baja | Media | Alta | | Expresividad | Baja | Alta | Muy alta | | Auditoría | Fácil | Compleja | Requiere herramientas | | Tooling externo | UnifyID, Auth0 roles | OPA, Cedar (AWS) | OpenFGA, SpiceDB | | Dominio típico | Apps internas, ERP | SaaS B2B con políticas finas | Apps colaborativas |

Combinarlos en Spring Security

Una aplicación real raramente usa solo uno. El patrón típico es:

  • RBAC para diferenciar superadmin / admin / usuario.
  • ABAC para reglas de dominio fino (mismo tenant, mismo equipo, propietario).
  • ReBAC opcional para colaboración compleja (compartir documento con un equipo).
@PreAuthorize("hasRole('ADMIN') or @politicas.puedeEditar(authentication, #id)")
public Documento actualizar(Long id, ActualizarCmd cmd) { ... }

La primera condición (RBAC) es rápida y atajo. La segunda (ABAC) entra solo si la primera falla.

Externalizar políticas con OPA

Open Policy Agent es un motor de políticas escritas en Rego que se ejecuta como sidecar. Spring Security llama a OPA antes de cada decisión sensible.

Política Rego (policy.rego):

package autorizacion

default allow = false

allow {
    input.user.role == "admin"
}

allow {
    input.action == "read"
    input.resource.owner == input.user.id
}

allow {
    input.action == "edit"
    input.user.tenant == input.resource.tenant
    input.user.scopes[_] == "documents:write"
}

Y desde Spring:

@Component
public class OpaAuthorizationManager {

    private final RestClient client;

    public boolean allow(String user, String action, Map<String, Object> resource) {
        Map<String, Object> input = Map.of(
            "user", Map.of("id", user, "role", currentRole(), "tenant", currentTenant()),
            "action", action,
            "resource", resource
        );
        OpaResponse resp = client.post()
            .uri("/v1/data/autorizacion/allow")
            .body(Map.of("input", input))
            .retrieve()
            .body(OpaResponse.class);
        return resp.result();
    }
}

OPA centraliza las políticas en un repositorio aparte. El equipo de seguridad las edita sin tocar el código de los microservicios.

Cedar: la apuesta de AWS

Cedar es el lenguaje de políticas que AWS lanzó en 2023 para Verified Permissions. Es más expresivo que Rego para casos típicos y tiene un sistema de tipos que evita errores comunes.

permit (
    principal in Group::"Editores",
    action == Action::"editar",
    resource is Articulo
) when {
    principal.tenant == resource.tenant &&
    resource.estado == "borrador"
};

AWS ofrece SDK Java (software.amazon.cedar:cedar-java) que evalúa políticas sin red.

Anti-patrones comunes

  • Hardcodear roles en el código: if (user.getRole().equals("ADMIN")) por toda la base de código. Centraliza la decisión en un único punto.
  • Confiar en principal sin validar el origen del JWT: si el JWT viene de un IdP externo, ata las autoridades a scopes y no a roles arbitrarios que el cliente puede poner.
  • No versionar políticas: cambios en la lógica de autorización tan críticos como cambios en el código. Versiona Rego, Cedar o las clases Java.
  • Mezclar autorización y autenticación: la autenticación dice quién eres, la autorización qué puedes hacer. Mantén las dos capas separadas y testables independientemente.

Decision tree para elegir modelo

  • ¿Tu app tiene menos de 10 roles y los recursos no varían por instancia? RBAC.
  • ¿Las decisiones dependen del estado del recurso o de atributos del usuario? ABAC.
  • ¿Los usuarios comparten recursos formando jerarquías o equipos? ReBAC.
  • ¿Las políticas las gestiona el equipo de seguridad y no los desarrolladores? OPA o Cedar externos.

Empezar simple es la regla. Migra a ABAC o ReBAC cuando RBAC ya no escale, no antes. Una app con 200 endpoints rara vez necesita un motor de políticas externo desde el día uno.

Alan Sastre - Autor del tutorial

Alan Sastre

Ingeniero de Software y formador, CEO en CertiDevs

Ingeniero de software especializado en Full Stack y en Inteligencia Artificial. Como CEO de CertiDevs, Spring Security es una de sus áreas de expertise. Con más de 15 años programando, 6K seguidores en LinkedIn y experiencia como formador, Alan se dedica a crear contenido educativo de calidad para desarrolladores de todos los niveles.

Más tutoriales de Spring Security

Explora más contenido relacionado con Spring Security y continúa aprendiendo con nuestros tutoriales gratuitos.

Aprendizajes de esta lección

Diferenciar autorización RBAC, ABAC y ReBAC, decidir cuál encaja en cada dominio de negocio e implementar las tres con Spring Security combinando hasRole, AuthorizationManager y motores externos como OpenFGA.