Entendendo a Camada de Avaliação no Spring Security
A camada de avaliação do Spring Security é o mecanismo responsável por decidir se uma requisição pode ou não acessar um recurso protegido. Ela fica entre o filtro de segurança e o método de negócio que você quer chamar. A classe central desse processo é o AccessDecisionManager, que consulta uma lista de AccessDecisionVoter para cada invocação segura. Cada voter opina com "acesso concedido", "negado" ou "não opinar". Se nenhum voter votar contra, o acesso é liberado. Na prática, isso significa que você pode ter dezenas de regras de acesso espalhadas em diferentes votantes, e todas precisam concordar (ou pelo menos nenhuma precisa negar, dependendo da estratégia) para que a chamada chegue ao seu serviço. Isso é poderoso, mas também é onde a maioria dos projetos começa a criar problemas de performance e manutenção silenciosos.
Como configurar a capa de avaliação primavera
Se você está usando Spring Boot 3.x com Spring Security 6+, a configuração mínima para ativar a camada de avaliação method-level é praticamente automática se você marcar a classe de configuração com @EnableMethodSecurity. Sem esse anotação, as anotações @PreAuthorize, @PostAuthorize e @Secured simplesmente não fazem nada. Já vigente muitos desenvolvedores perderem horas tentando entender por que suas regras de segurança não estavam sendo aplicadas, e a resposta era quase sempre essa anotação esquecida. Para configurar explicitamente, você cria um bean do tipo AuthorizationManager (o sucessor do antigo AccessDecisionManager no Spring Security 6) e o integra ao seu SecurityFilterChain.
Veja um exemplo funcional:
@Configuration
@EnableMethodSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(auth -> auth
.requestMatchers("/public/").permitAll()
.anyRequest().authenticated()
)
.httpBasic(Customizer.withDefaults());
return http.build();
}
@Bean
public AuthorizationManager authorizationManager() {
return new AuthorityAuthorizationManager(List.of("ROLE_ADMIN"));
}
}
Esse código garante que apenas usuários com a role ROLE_ADMIN consigam acessar os endpoints protegidos pela autorização. Para métodos, o @PreAuthorize("hasRole('ADMIN')") faz o trabalho dentro do seu serviço.
👉 Clique no botão abaixo para saber mais sobre o assunto!
Um problema real que encontrei
Em um projeto interno, tínhamos um endpoint que verificava permissões baseado em uma regra de negócio complexa: o usuário só podia acessar se fosse responsável pelo setor e o setor estivesse ativo. A primeira implementação foi colocar toda essa lógica dentro de um @PreAuthorize com uma expressão SpEL gigante. Funcionou. Também gerou um gargalo perceptível nos momentos de pico — cada requisição disparava uma consulta ao banco para verificar o setor, mesmo quando o usuário já tinha sido autenticado e autorizado há menos de um segundo atrás. A solução foi criar um votante personalizado que recebia o objeto de assinatura completo, consultava o cache local (que mantinha os dados do usuário e seus setores por 30 segundos) e emitia o voto. O votante personalizado substituiu a expressão SpEL e reduziu as consultas ao banco de cerca de 200 por minuto para menos de 5. A regra de negócio permaneceu a mesma, mas a execução ficou muito mais enxuta.
Pegadinhas que ninguém conta
O primeiro problema invisível é que o Order dos votantes importa. Se você tem um votante genérico configurado com ordem menor que um votante específico, o genérico vota primeiro e, se usar a estratégia ACCESS_ABSTAIN, pode encerrar a avaliação prematuramente. O votante específico nunca chega a opinar. Verifique sempre o ordering dos seus beans de votação. O segundo problema é mais sutil: a avaliação ocorre duas vezes se você combinar @PreAuthorize com AccessDecisionVoter configurados no mesmo escopo. O Spring Security avalia na camada de método e depois na camada de URL. Isso significa que uma regra duplicada gera um voto duas vezes. Não quebre nada, mas se você estiver debugando comportamentos estranhos de permissão, esse duplo-processamento pode ser a causa.
Outro ponto que gera confusão: o voter abstém-se, não vota negativamente. Se todos os votantes absterem-se e a estratégia for UNANIMOUS, o acesso é concedido por padrão. Isso é intencional no design original do Spring Security, mas muitas equipes assumem o contrário e ficam frustradas quando regras "invisíveis" liberam acesso. Se quiser comportamento rigoroso, configure a estratégia como CONSENSUS com allowIfAllAbstainDecisions = false.
Alternativas quando a camada de avaliação não é suficiente
Se suas regras de autorização envolvem relacionamentos complexos (herança de roles, políticas baseadas em attributes dinâmicos, verificações em tempo real que não cabem em SpEL), a camada de avaliação pura do Spring Security vai começar a mostrar limitações. Nesse cenário, faz sentido sair da expressão SpEL e implementar um AuthorizationService customizado que recebe o contexto completo e toma a decisão antes de passar para o Spring Security. A desvantagem é que você perde a integração automática com a camada de URL, mas ganha controle total sobre a lógica. Para a maioria dos casos, a configuração padrão com @EnableMethodSecurity e um AuthorizationManager bem estruturado resolve. O segredo está em manter os votantes simples, cache quando possível e evitar expressões SpEL que disparam chamadas de serviço diretamente.