Autorização baseada em URL — authorizeHttpRequests, roles vs authorities
TL;DR
Autenticação responde “quem é você?”; autorização responde “o que você pode acessar?“. Depois que o usuário já está autenticado, a autorização baseada em URL decide quais requests ele pode tocar. O método atual é
authorizeHttpRequests(que substituiu oauthorizeRequests, hoje legado): você casa requests comrequestMatcherse amarra cada padrão a uma regra —permitAll(),authenticated(),hasRole(...),hasAuthority(...),denyAll(). Detalhe que pega todo mundo:hasRole("ADMIN")busca a authorityROLE_ADMIN— o prefixoROLE_é convenção e o Security adiciona sozinho, então nunca repita o prefixo. A ordem dos matchers importa: do mais específico para o mais genérico, comanyRequest()por último.
O que é
Autorização baseada em URL é a camada que, request a request, decide se o usuário autenticado tem permissão para chegar ao endpoint. Ela roda no AuthorizationFilter (no Security 6.x), bem dentro do filter chain, e funciona como uma lista de pares “padrão de URL → regra de acesso”.
Você descreve essa lista dentro de um @Bean SecurityFilterChain, usando o DSL do HttpSecurity:
authorizeHttpRequests(...)abre o bloco de autorização por URL.requestMatchers(...)descreve quais requests aquela regra cobre (por path, porHttpMethod, ou ambos).- Métodos como
permitAll(),authenticated(),hasRole(...),hasAnyRole(...),hasAuthority(...)edenyAll()dizem o que é exigido para passar.
O AuthorizationFilter percorre a lista na ordem em que você declarou e aplica a primeira regra que casar com o request — por isso a ordem é parte da semântica, não detalhe estético.
Roles vs authorities em uma frase
Uma authority é qualquer permissão granular (
orders:read,db). Uma role é só uma authority com o prefixo convencionalROLE_(ROLE_ADMIN).hasRole("ADMIN")é açúcar para “tem a authorityROLE_ADMIN”.
Por que importa
Sem essa camada, qualquer usuário autenticado acessaria qualquer endpoint — autenticação sozinha não separa um Customer comum de um administrador. A autorização por URL é a primeira linha de defesa, declarativa e centralizada, e cobre casos clássicos:
- Endpoints públicos (login, healthcheck, página “sobre”) liberados com
permitAll(). - Áreas administrativas restritas a quem tem
ROLE_ADMIN. - Endpoints de gestão (Actuator) protegidos para não vazar métricas, configs e beans para qualquer um.
- Regras por método HTTP: ler é mais liberal, escrever exige mais privilégio.
É também o ponto onde mais se erra em entrevista e em produção: confundir role com authority, duplicar o prefixo ROLE_, ou ordenar os matchers de forma que uma regra genérica “engole” uma específica.
Como funciona
authorizeHttpRequests + requestMatchers (o que substituiu authorizeRequests/antMatchers)
No Spring Security 6.x / Spring Boot 3.x, o DSL canônico é authorizeHttpRequests, que liga o AuthorizationFilter. Ele substituiu o antigo authorizeRequests (que usava o FilterSecurityInterceptor e hoje está deprecated/removido).
Dentro dele, os padrões de URL são descritos por requestMatchers(...), que substituiu antMatchers e mvcMatchers. Um único requestMatchers cobre ambos os mundos: você passa o path (/api/admin/**) ou um HttpMethod mais o path.
http.authorizeHttpRequests(auth -> auth
.requestMatchers("/api/public/**").permitAll()
.anyRequest().authenticated());Cada linha é um par “matcher → regra”. O filtro avalia de cima para baixo e para na primeira que bater.
hasRole vs hasAuthority: o prefixo ROLE_ e quando usar cada
Esta é a distinção que mais cai:
hasAuthority("orders:read")exige a authority exatamente como escrita. Nada é adicionado.hasRole("ADMIN")exige a authorityROLE_ADMIN. O Security adiciona o prefixoROLE_automaticamente — por isso você passa só"ADMIN".
Ou seja, hasRole("ADMIN") é equivalente a hasAuthority("ROLE_ADMIN"). São duas formas de exigir a mesma authority; hasRole é o atalho que assume a convenção ROLE_.
Regra prática
Use
hasRolepara papéis amplos (ADMIN, USER, MANAGER) ehasAuthoritypara permissões granulares (orders:read,orders:write). E nunca escreva o prefixo dentro dehasRole— ele já está implícito.
RBAC básico: roles → acesso
RBAC (Role-Based Access Control) é o modelo onde o acesso é decidido pela role do usuário, não pela identidade individual. O fluxo é:
- O usuário se autentica e recebe um conjunto de authorities (ex.:
ROLE_USER,ROLE_ADMIN). - Cada endpoint declara qual role/authority exige.
- O
AuthorizationFiltercruza as authorities do usuário com a exigência da rota.
Em termos de domínio: um Customer carrega ROLE_USER e enxerga seus próprios Orders; um operador carrega ROLE_ADMIN e acessa /api/admin/**. As authorities vêm do UserDetails (ver as notas de autenticação do galho). A autorização por URL é o lado consumidor desse RBAC.
Granularidade
RBAC por URL cobre o “quadro geral” (quem entra onde). Para regras mais finas — “só o dono daquele
Order” — entra a method security e a autorização avançada.
Ordem dos matchers e matcher por HttpMethod
O AuthorizationFilter aplica a primeira regra que casar. Logo, declare os matchers do mais específico para o mais genérico, e deixe anyRequest() por último como rede de segurança (.anyRequest().authenticated() ou .anyRequest().denyAll()).
Se você colocar um matcher genérico antes de um específico, o específico nunca é alcançado — a regra genérica casa primeiro.
Você também pode restringir por método HTTP passando um HttpMethod:
.requestMatchers(HttpMethod.GET, "/api/orders/**").hasAuthority("orders:read")
.requestMatchers(HttpMethod.POST, "/api/orders/**").hasAuthority("orders:write")Assim, GET (leitura) e POST (escrita) no mesmo path exigem authorities diferentes — um padrão comum de separar leitura de mutação.
Na prática
Um SecurityFilterChain típico amarrando tudo: público liberado, área admin e Actuator restritos a ROLE_ADMIN, leitura de pedidos por authority granular, e o resto exigindo autenticação.
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.web.SecurityFilterChain;
@Configuration
public class SecurityConfig {
@Bean
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(auth -> auth
// 1. público: liberado para todos
.requestMatchers("/api/public/**").permitAll()
// 2. área administrativa: exige a authority ROLE_ADMIN
.requestMatchers("/api/admin/**").hasRole("ADMIN")
// 3. endpoints de gestão (Actuator) também só para admin
.requestMatchers("/actuator/**").hasRole("ADMIN")
// 4. leitura de pedidos: authority granular por método
.requestMatchers(HttpMethod.GET, "/api/orders/**").hasAuthority("orders:read")
// 5. rede de segurança: tudo o mais exige usuário autenticado
.anyRequest().authenticated())
.httpBasic(withDefaults -> {});
return http.build();
}
}Pontos a notar:
/actuator/**está protegido comhasRole("ADMIN")— sem isso, métricas, env e beans ficariam expostos. Combine com a configuração de exposição do Actuator.hasRole("ADMIN")buscaROLE_ADMIN(sem repetir o prefixo).hasAuthority("orders:read")exige a authority literal — útil para permissões finas.- A ordem vai do específico (
/api/public/**,/api/admin/**) ao genérico (anyRequest()).
Armadilhas
(1) Usar antMatchers/authorizeRequests legados
authorizeRequests, antMatchers e mvcMatchers foram substituídos no Security 6.x (o authorizeRequests está deprecated/removido junto com o FilterSecurityInterceptor). Código copiado de tutoriais antigos simplesmente não compila ou não tem o comportamento esperado.
// ERRADO (API legada do Security 5.x)
http.authorizeRequests(auth -> auth
.antMatchers("/api/admin/**").hasRole("ADMIN"));Fix: use o DSL atual.
// CERTO (Security 6.x)
http.authorizeHttpRequests(auth -> auth
.requestMatchers("/api/admin/**").hasRole("ADMIN"));(2) Duplicar o prefixo ROLE_ em hasRole
hasRole já adiciona ROLE_. Se você escrever hasRole("ROLE_ADMIN"), o Security passa a procurar a authority ROLE_ROLE_ADMIN, que ninguém tem — e todo mundo recebe 403.
// ERRADO: vira ROLE_ROLE_ADMIN
.requestMatchers("/api/admin/**").hasRole("ROLE_ADMIN")Fix: passe só o nome da role; se precisar mesmo da authority com prefixo, use hasAuthority.
// CERTO
.requestMatchers("/api/admin/**").hasRole("ADMIN")
// ou, explicitamente:
.requestMatchers("/api/admin/**").hasAuthority("ROLE_ADMIN")(3) Ordem errada dos matchers
Como vale a primeira regra que casa, um matcher genérico colocado antes de um específico “engole” o específico, que nunca é avaliado.
// ERRADO: anyRequest casa /api/admin/** antes da regra de ADMIN
.anyRequest().authenticated()
.requestMatchers("/api/admin/**").hasRole("ADMIN") // inalcançávelFix: do mais específico para o mais genérico, com anyRequest() por último.
// CERTO
.requestMatchers("/api/admin/**").hasRole("ADMIN")
.anyRequest().authenticated()(4) permitAll() num endpoint sensível por engano
Um requestMatchers("/api/**").permitAll() colocado para “destravar testes” e esquecido em produção libera a API inteira. Pior: por casar cedo e ser genérico, ele ainda anula regras mais restritivas que viessem depois.
// ERRADO: libera tudo sob /api, inclusive /api/admin
.requestMatchers("/api/**").permitAll()Fix: seja cirúrgico no permitAll() (apenas paths realmente públicos) e mantenha o default fechado com anyRequest().authenticated() ou denyAll().
// CERTO
.requestMatchers("/api/public/**").permitAll()
.anyRequest().authenticated()Em entrevista
Frase pronta (inglês)
In Spring Security 6, I configure URL-based authorization with
authorizeHttpRequests, which replaced the oldauthorizeRequests, and I match requests usingrequestMatchersinstead of the deprecatedantMatchers. Each matcher is bound to a rule such aspermitAll,authenticated,hasRole, orhasAuthority, and theAuthorizationFilterapplies the first matcher that matches, so I always order them from most specific to most generic withanyRequestlast. One detail I’m careful about is thathasRole("ADMIN")actually checks for the authorityROLE_ADMIN— theROLE_prefix is a convention added automatically, so I never repeat it. For coarse, role-based access I usehasRole, and for fine-grained permissions likeorders:readI usehasAuthority.
Vocabulário
| Termo (EN) | Tradução / sentido |
|---|---|
| URL-based authorization | autorização baseada em URL |
| request matcher | casador de requisição (requestMatchers) |
| role | papel (authority com prefixo ROLE_) |
| authority | permissão granular |
ROLE_ prefix convention | convenção do prefixo ROLE_ |
| coarse-grained access | acesso de granularidade grossa (por role) |
| fine-grained permission | permissão de granularidade fina |
| catch-all rule | regra “pega-tudo” (anyRequest()) |
| filter chain | cadeia de filtros |
Veja também
- O que é Spring Security
- Method security
- Autorização avançada
- Actuator e observabilidade (proteger os endpoints)
- Segurança (MOC do galho)
- Trilha Java
- Dicionário de Java (verbetes authorizeHttpRequests / hasRole vs hasAuthority / RBAC)
Referências
- Spring Security Reference — Authorize HttpServletRequests: https://docs.spring.io/spring-security/reference/servlet/authorization/authorize-http-requests.html