iGRPIGRP 3.0Documentation

Permissões no Backend

Declarar permissões de negócio com @IgrpPermission, sincronizar com o Access Management e aplicá-las com @PreAuthorize.

Destinatários: developers a construir um projeto de API de negócio — qualquer serviço Spring Boot que precise de declarar as suas próprias permissões e tê-las aplicadas através da API central do iGRP Access Management.

Este guia cobre o ciclo de vida completo do lado do projeto alvo:

  1. Declarar as permissões de negócio no código com @IgrpPermission.
  2. Compilar — o processador de anotações da framework gera PermissionsRegistry.Permission em target/generated-sources/.
  3. Arrancar — a autoconfig AuthorizationSyncRunner sincroniza o catálogo de recursos e permissões no Access Management.
  4. Em tempo de pedido — os JWTs emitidos pelo Access Management transportam as permissões concedidas como authorities; @PreAuthorize (ou o separador Security do iGRP Studio) aplica-as.

Nada neste guia é interno ao Access Management. Aplica-se a qualquer serviço Spring Boot que inclua cv.igrp.framework.auth:core-spring-boot.


1. O panorama geral

BUILDARRANQUEO teu código@IgrpPermissionServiço Spring Bootcore-spring-bootAccess Managementservidor OAuth2mvn compile → PermissionsRegistryPOST /oauth2/tokenaccess token (client_credentials)POST /api/m2m/sync/resourcesPOST /api/m2m/sync/permissions
Escreves só as anotações. No build, o processador gera o PermissionsRegistry; a cada arranque, o AuthorizationSyncRunner autentica-se com client_credentials e regista recursos e permissões no Access Management.

Escreves só as anotações. Os JARs da framework fornecem tudo o resto — o processador de anotações, o runner de autoconfig, o conversor de JWT e o serviço SpEL. Só precisas da configuração que este guia detalha. O que acontece a cada pedido está em Endpoints & Troubleshooting.


2. Configuração Maven

2.1 Bloco de propriedades

Fixa as versões da framework num único lugar para que todos os módulos do projeto concordem:

<properties>
    <java.version>26</java.version>
    <framework.auth.version>0.2.0-beta.9</framework.auth.version>
    <!-- Só necessário se mantiveres um bloco <annotationProcessorPaths> explícito (ver §2.4).
         core é lançado independentemente de core-spring-boot e a sua última versão é 0.2.0-beta.8;
         NÃO reutilizes ${framework.auth.version}. -->
    <framework.auth.core.version>0.2.0-beta.8</framework.auth.core.version>
</properties>

Não fixes as dependências transitivas

Não precisas de fixar cv.igrp.framework:stereotype (que define @IgrpPermission) nem cv.igrp.framework.auth:core (que contém o processador de anotações) — ambos vêm transitivamente por core-spring-boot. Declará-los outra vez só arrisca um desalinhamento com as versões contra as quais core-spring-boot foi construído. A única razão para framework.auth.core.version existir acima é o caminho explícito de processador de anotações da §2.4.

2.2 Repositórios

Os JARs da framework vivem no Nexus do iGRP, não no Maven Central. Adiciona os repositórios e o espelho pluginRepository, para que o processador de anotações possa ser resolvido durante a compilação:

<repositories>
    <repository>
        <id>igrp-framework-releases</id>
        <url>https://sonatype.nosi.cv/repository/igrp-framework-releases/</url>
    </repository>
    <repository>
        <id>igrp-framework-snapshots</id>
        <url>https://sonatype.nosi.cv/repository/igrp-framework/</url>
        <snapshots><enabled>true</enabled></snapshots>
    </repository>
</repositories>

<pluginRepositories>
    <pluginRepository>
        <id>igrp-framework-releases</id>
        <url>https://sonatype.nosi.cv/repository/igrp-framework-releases/</url>
    </pluginRepository>
</pluginRepositories>

2.3 Dependências de runtime

Adiciona uma dependência iGRP — core-spring-boot. Traz transitivamente:

  • cv.igrp.framework.auth:core — o processador de anotações, IgrpAuthorizationService, IgrpJwtAuthenticationConverter, MethodSecurityConfig e a auto-configuração.
  • cv.igrp.framework:stereotype — a anotação @IgrpPermission.
  • cv.igrp.platform.access:client — o ApiClient / M2MApi usado pelo AuthorizationSyncRunner.
  • spring-boot-starter-oauth2-resource-server — validação de JWT.

Não redeclares nenhum desses artefactos no teu pom.xml — só convida ao desalinhamento de versões.

<dependencies>
    <!-- Tudo o que este guia descreve vem daqui. -->
    <dependency>
        <groupId>cv.igrp.framework.auth</groupId>
        <artifactId>core-spring-boot</artifactId>
        <version>${framework.auth.version}</version>
    </dependency>

    <!-- Starters Spring Boot que a tua API usa diretamente. -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-security</artifactId>
    </dependency>
</dependencies>

Um pom.xml mínimo completo — Spring Boot parent, só a dependência iGRP e os dois starters, sem overrides de <dependencyManagement> — é a referência por onde todo o projeto novo deve começar:

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>3.5.14</version>
    <relativePath/>
</parent>

<properties>
    <java.version>26</java.version>
    <framework.auth.version>0.2.0-beta.9</framework.auth.version>
</properties>

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-security</artifactId>
    </dependency>
    <dependency>
        <groupId>cv.igrp.framework.auth</groupId>
        <artifactId>core-spring-boot</artifactId>
        <version>${framework.auth.version}</version>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>

2.4 Processador de anotações

Na maioria dos projetos não é necessário nada extra. @IgrpPermission (de stereotype) e o processador PermissionSourceGenerator (de core) vêm ambos transitivamente por core-spring-boot, pelo que em tempo de compilação o processador é descoberto automaticamente via META-INF/services/javax.annotation.processing.Processor. Corre mvn clean compile e confirma que aparece:

target/generated-sources/annotations/cv/igrp/framework/auth/generated/PermissionsRegistry.java

Se já declaras um bloco <annotationProcessorPaths> para outros processadores (Lombok, MapStruct, …), o Maven usa só esse bloco e deixa de fazer scan automático do classpath. Nesse caso, adiciona core explicitamente (traz stereotype transitivamente, logo uma entrada cobre os dois):

<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <configuration>
                <source>26</source>
                <target>26</target>
                <encoding>UTF-8</encoding>
                <compilerArgs>
                    <arg>-parameters</arg>
                </compilerArgs>

                <annotationProcessorPaths>
                    <!-- Lombok primeiro, se o usares -->
                    <path>
                        <groupId>org.projectlombok</groupId>
                        <artifactId>lombok</artifactId>
                    </path>

                    <!-- Gera PermissionsRegistry.java.
                         core traz transitivamente cv.igrp.framework:stereotype,
                         que define a própria @IgrpPermission.
                         core é lançado independentemente de core-spring-boot —
                         usa a sua própria propriedade de versão. -->
                    <path>
                        <groupId>cv.igrp.framework.auth</groupId>
                        <artifactId>core</artifactId>
                        <version>${framework.auth.core.version}</version>
                    </path>
                </annotationProcessorPaths>
            </configuration>
        </plugin>
    </plugins>
</build>

Se PermissionsRegistry.java não aparecer após um clean compile, a causa é quase sempre: (a) introduziste um bloco <annotationProcessorPaths> sem adicionar core, ou (b) o compilador tem <proc>none</proc>.

Opcional — declaração estática do processador

Se preferires fixar os processadores a classes concretas — vários JARs de processadores no path e queres ordenação determinística, ou queres contornar a descoberta por SPI — usa <annotationProcessors> a par do bloco de caminhos:

<configuration>
    <annotationProcessorPaths>
        <!-- ... igual ao anterior ... -->
    </annotationProcessorPaths>

    <annotationProcessors>
        <annotationProcessor>lombok.launch.AnnotationProcessorHider$AnnotationProcessor</annotationProcessor>
        <annotationProcessor>cv.igrp.framework.auth.core.processor.PermissionSourceGenerator</annotationProcessor>
    </annotationProcessors>
</configuration>

Prefere a forma implícita (descoberta por SPI) a não ser que tenhas uma razão concreta.

2.5 Ligação das generated-sources à IDE

Depois de mvn compile, a IDE precisa de tratar target/generated-sources/annotations como source root, para que as referências a PermissionsRegistry.Permission.* resolvam:

<plugin>
    <groupId>org.codehaus.mojo</groupId>
    <artifactId>build-helper-maven-plugin</artifactId>
    <executions>
        <execution>
            <id>add-generated-source</id>
            <phase>generate-sources</phase>
            <goals><goal>add-source</goal></goals>
            <configuration>
                <sources>
                    <source>${project.build.directory}/generated-sources/annotations</source>
                </sources>
            </configuration>
        </execution>
    </executions>
</plugin>

O IntelliJ costuma apanhar annotations automaticamente; num clone novo faz Maven → Reload All Projects, ou clica com o botão direito na pasta annotations → Mark Directory as → Generated Sources Root.


3. Declarar permissões com @IgrpPermission

A anotação vive em cv.igrp.framework.stereotype e aplica-se a campos estáticos:

// src/main/java/cv/igrp/demo/shared/authorization/InvoicePermissions.java
package cv.igrp.demo.shared.authorization;

import cv.igrp.framework.stereotype.IgrpPermission;

public final class InvoicePermissions {

    private InvoicePermissions() {}

    @IgrpPermission(name = "my.invoice.create", description = "Criar uma fatura")
    public static final String INVOICE_CREATE = "my.invoice.create";

    @IgrpPermission(name = "my.invoice.view", description = "Consultar uma fatura")
    public static final String INVOICE_VIEW = "my.invoice.view";

    @IgrpPermission(name = "my.invoice.approve", description = "Aprovar uma fatura", enabled = true)
    public static final String INVOICE_APPROVE = "my.invoice.approve";

    @IgrpPermission(name = "my.invoice.export", description = "Exportar para CSV/PDF", enabled = false)
    public static final String INVOICE_EXPORT = "my.invoice.export";
}

Convenções que compensam:

  • name é o código canónico da permissão — sensível a maiúsculas, minúsculas pontuadas, com namespace do teu contexto delimitado. É o que fica guardado no Access Management e o que aparece no JWT.
  • O valor da constante String deve ser igual ao name, byte por byte. A constante dá autocomplete na IDE nos controladores (strings de @PreAuthorize) e permite que ferramentas fora do mundo das anotações referenciem o mesmo identificador.
  • description é o que os administradores vêem na UI do Access Management ao atribuir a permissão a um perfil. Escreve-a como uma frase.
  • enabled = false registra a permissão no Access Management como INACTIVE — útil para permissões que queres que os administradores descubram mas ainda não concedam.
  • Uma classe por contexto delimitado. Não despejes todas as permissões de todos os domínios num ficheiro — torna os diffs ilegíveis e os conflitos de merge inevitáveis em equipas grandes.

O que o processador de anotações gera

Em mvn compile, o PermissionSourceGenerator (de cv.igrp.framework.auth:core) percorre todas as @IgrpPermission que consegue ver e escreve um único ficheiro:

target/generated-sources/annotations/cv/igrp/framework/auth/generated/PermissionsRegistry.java

…com esta forma:

package cv.igrp.framework.auth.generated;

public final class PermissionsRegistry {

    public enum Permission {
        MY_INVOICE_CREATE ("my.invoice.create", "Criar uma fatura",     true),
        MY_INVOICE_VIEW   ("my.invoice.view",   "Consultar uma fatura", true),
        MY_INVOICE_APPROVE("my.invoice.approve","Aprovar uma fatura",   true),
        MY_INVOICE_EXPORT ("my.invoice.export", "Exportar para CSV/PDF",false);

        private final String code;
        private final String description;
        private final boolean enabled;

        Permission(String code, String description, boolean enabled) {
            this.code = code;
            this.description = description;
            this.enabled = enabled;
        }

        public String getCode()        { return code; }
        public String getDescription() { return description; }
        public boolean enabled()       { return enabled; }
    }
}

O nome da constante do enum deriva de name() (maiúsculas, não alfanuméricos → _).

Nomes duplicados são silenciosamente descartados

Valores de name() duplicados (comparação insensível a maiúsculas) produzem um aviso do compilador e a segunda ocorrência é descartada em silêncio — só a primeira @IgrpPermission com aquele nome sobrevive. Trata duplicados como bugs.

Se description ficar vazia, o processador usa o valor de name como descrição. O name() é obrigatório (a anotação não declara default), pelo que um nome em branco falha em tempo de compilação.

Se o ficheiro não aparecer em target/generated-sources/annotations/ depois de um clean compile, a causa é quase sempre o caminho do processador de anotações da §2.4.


4. Sincronização com o Access Management no arranque

O core-spring-boot inclui um AuthorizationSyncRunner autoconfigurado que dispara em @PostConstruct a cada arranque da aplicação. Ele:

  1. Autentica-se no Access Management com OAuth2 client_credentials em POST /oauth2/token (com as credenciais da §5).
  2. Lê todas as constantes do PermissionsRegistry.Permission gerado.
  3. Faz POST /api/m2m/sync/resources para registar o teu serviço como Resource (type=API, name=${spring.application.name}). O cliente M2MApi envia spring.application.name junto com o recurso, para que o Access Management possa ligar o registo à aplicação chamadora.
  4. Faz POST /api/m2m/sync/permissions com a lista completa de PermissionDTO — name=code, description, status=ACTIVE|INACTIVE.

A sincronização é idempotente e auto-corretiva: permissões definidas localmente e ausentes no Access Management são inseridas; permissões apagadas localmente e ainda presentes no Access Management não são removidas automaticamente (os administradores podem já as ter atribuído a perfis — a remoção é uma ação administrativa explícita). Texto novo em description sobrepõe o valor guardado.

Uma falha de sincronização não impede o arranque

O runner captura o erro e registra em ERROR. É deliberado, para que uma indisponibilidade transitória do Access Management não derrube todos os serviços consumidores. O preço: um cliente M2M mal configurado produz linhas [Permission Sync] Failed to synchronize… repetidas até alguém notar. Liga esse log a alerting.

Não há propriedade para desligar o runner

O runner é um @Component simples em @PostConstruct — não existe propriedade para o desativar. Se precisares de o saltar em testes, exclui AuthorizationSyncRunner via @SpringBootApplication(exclude = …) ou usa um test slice que não faça scan de cv.igrp.framework.auth.core.autoconfig.

4.1 Catálogo completo de endpoints M2M

Além do trio que o runner usa, o Access Management expõe endpoints M2M adicionais que os projetos alvo podem chamar via M2MApi:

Método + caminhoPropósito
POST /api/m2m/sync/permissionsSincronizar o catálogo de permissões (usado pelo AuthorizationSyncRunner).
POST /api/m2m/sync/resourcesRegistar/atualizar esta API como Resource (usado pelo runner).
POST /api/m2m/sync/applicationsRegistar/atualizar o chamador como Application.
POST /api/m2m/sync/applications/{code}/menus?syncRoles=trueEnviar a árvore de menus do frontend (usado pelo lado Next.js).
POST /api/m2m/usersConsultar utilizadores de negócio IGRP (filtros no corpo).
POST /api/m2m/departmentsConsultar departamentos de negócio IGRP.
POST /api/m2m/rolesConsultar perfis de negócio IGRP.

Todas as chamadas exigem um bearer token obtido por client_credentials em /oauth2/token, com um scope permitido pelo registo do cliente M2M.



Documentação complementar

How is this guide?

Última atualização: