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:
- Declarar as permissões de negócio no código com
@IgrpPermission. - Compilar — o processador de anotações da framework gera
PermissionsRegistry.Permissionemtarget/generated-sources/. - Arrancar — a autoconfig
AuthorizationSyncRunnersincroniza o catálogo de recursos e permissões no Access Management. - 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
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,MethodSecurityConfige a auto-configuração.cv.igrp.framework:stereotype— a anotação@IgrpPermission.cv.igrp.platform.access:client— oApiClient/M2MApiusado peloAuthorizationSyncRunner.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.javaSe 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 = falseregistra a permissão no Access Management comoINACTIVE— ú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:
- Autentica-se no Access Management com OAuth2
client_credentialsemPOST /oauth2/token(com as credenciais da §5). - Lê todas as constantes do
PermissionsRegistry.Permissiongerado. - Faz
POST /api/m2m/sync/resourcespara registar o teu serviço comoResource(type=API,name=${spring.application.name}). O clienteM2MApienviaspring.application.namejunto com o recurso, para que o Access Management possa ligar o registo à aplicação chamadora. - Faz
POST /api/m2m/sync/permissionscom a lista completa dePermissionDTO—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 + caminho | Propósito |
|---|---|
POST /api/m2m/sync/permissions | Sincronizar o catálogo de permissões (usado pelo AuthorizationSyncRunner). |
POST /api/m2m/sync/resources | Registar/atualizar esta API como Resource (usado pelo runner). |
POST /api/m2m/sync/applications | Registar/atualizar o chamador como Application. |
POST /api/m2m/sync/applications/{code}/menus?syncRoles=true | Enviar a árvore de menus do frontend (usado pelo lado Next.js). |
POST /api/m2m/users | Consultar utilizadores de negócio IGRP (filtros no corpo). |
POST /api/m2m/departments | Consultar departamentos de negócio IGRP. |
POST /api/m2m/roles | Consultar 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
- Configuração de Segurança & Env Vars —
SecurityConfig,IAMUserProfileSyncFiltere variáveis de ambiente - Proteger Endpoints, Checklist & Troubleshooting —
@PreAuthorize, checklist ponta-a-ponta e resolução de problemas
Última atualização:
