O que é Spring Security — authn, authz e o filter chain
TL;DR
Spring Security protege a aplicação através de um filter chain — uma cadeia de filtros Servlet que intercepta cada request antes que ele chegue ao
DispatcherServlet. Ele separa dois conceitos que nunca devem se misturar: autenticação (authn — quem você é) e autorização (authz — o que você pode fazer). A configuração moderna na versão 6.x é um beanSecurityFilterChainescrito com lambda DSL e anotado com@EnableWebSecurity— o velhoWebSecurityConfigurerAdapterfoi removido na 6.0 e não existe mais.
O que é
Spring Security é o framework de segurança do ecossistema Spring. Ele resolve dois problemas distintos, e a primeira coisa a entender é que eles são distintos:
- Autenticação (authentication, authn) — responde “quem é você?“. É o processo de provar identidade: usuário e senha, token, certificado. O resultado é um
Authenticationpopulado no contexto. - Autorização (authorization, authz) — responde “o que você tem permissão de fazer?“. Dado que já sabemos quem você é, decide se você pode acessar este endpoint, este método, este recurso.
Você sempre autentica primeiro e autoriza depois — não dá pra decidir o que alguém pode fazer sem antes saber quem é. Spring Security materializa essa ordem na própria cadeia de filtros: o filtro que autentica roda antes do filtro que autoriza.
Por que importa
Toda aplicação séria exposta na web precisa decidir quem entra e o que cada um faz. Spring Security é a resposta padrão para isso no mundo Spring, e por ser tão onipresente, entrevista cobra. As perguntas clássicas são:
- “O que é o filter chain do Spring Security?”
- “Como uma request flui até o controller passando pela segurança?”
- “Qual a diferença entre autenticação e autorização?”
Quem responde “Spring Security é a tela de login” perdeu a questão. A tela de login é um filtro dentro de uma cadeia inteira. Entender a cadeia — e onde ela se encaixa em relação ao DispatcherServlet — é o que separa a resposta de iniciado da resposta de quem realmente conhece o framework.
Como funciona
Autenticação vs autorização: dois conceitos, não misturar
Pense num prédio comercial:
- Autenticação é o crachá na recepção: você prova que é o João da empresa X. A recepção não decide pra onde você vai — só confirma que você é o João.
- Autorização é o leitor de crachá em cada porta: o crachá do João abre o 3º andar mas não o data center. Agora que já sabemos que você é o João, decidimos a cada porta o que ele pode abrir.
São camadas diferentes, com momentos diferentes e perguntas diferentes. Misturar as duas — por exemplo, tratar “está logado?” como se fosse “pode fazer isso?” — é um dos erros mais comuns (ver Armadilhas).
O filter chain: request → FilterChainProxy → SecurityFilterChain → DispatcherServlet
Spring Security é construído sobre Servlet Filters. Um Filter genérico intercepta requests antes do servlet — esse mecanismo é da camada web (ver Interceptors vs Filters). Spring Security usa esse ponto de entrada para inserir sua própria cadeia.
O caminho de uma request é:
- O container cria a
FilterChainpadrão e passa o request adiante. - Um
DelegatingFilterProxy(registrado no container) faz a ponte do mundo Servlet para o mundo Spring, delegando para um bean gerenciado pelo container de Spring. - Esse bean é o
FilterChainProxy— o filtro central de Spring Security. Ele é o ponto único de entrada de toda a segurança e o melhor lugar para começar a depurar. - O
FilterChainProxyescolhe, viaRequestMatcher, qualSecurityFilterChainaplicar a este request (pode haver várias cadeias configuradas — só a primeira que casa é usada). - A
SecurityFilterChainescolhida define a lista ordenada de filtros de segurança a executar (autenticação, autorização, proteções contra exploits como CSRF). - Se o request sobreviver à cadeia (autenticado e autorizado), ele finalmente chega ao
DispatcherServlete segue o fluxo normal de MVC — ver O pipeline do DispatcherServlet.
Os filtros dentro da cadeia têm ordem definida — por exemplo, BasicAuthenticationFilter (autenticação via HTTP Basic) e UsernamePasswordAuthenticationFilter (form login) rodam antes do AuthorizationFilter (autorização). A ordem não é acidental: autenticação precede autorização.
Config 6.x: o bean SecurityFilterChain + lambda DSL (WebSecurityConfigurerAdapter foi removido na 6.0)
Na versão 6.x (baseline: Spring Boot 3.x, namespace jakarta.*, Java 17), a configuração é um bean que retorna uma SecurityFilterChain, construído com a lambda DSL do HttpSecurity:
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(auth -> auth
.anyRequest().authenticated()
)
.httpBasic(Customizer.withDefaults());
return http.build();
}
}O ponto crucial: até a versão 5.x você estendia uma classe abstrata, WebSecurityConfigurerAdapter, e sobrescrevia métodos. Essa classe foi removida na 6.0. Não existe mais. A forma correta hoje é declarar o bean SecurityFilterChain como acima. Código antigo (ou tutorial antigo) que manda estender WebSecurityConfigurerAdapter está desatualizado e não compila na 6.x.
E as versões mais novas?
A 6.x é o baseline para entrevistas atuais. Já existem Spring Security 7.x / Spring Boot 4.x como linhas mais recentes, mas o padrão
SecurityFilterChain+ lambda DSL é o mesmo — o que mudou foi o adapter sumir, e isso já aconteceu na 6.0.
A dupla fronteira: o chain na frente do DispatcherServlet (Galho 9), method security sobre AOP (Galho 8)
Spring Security age em duas fronteiras complementares:
- Na borda HTTP — o filter chain senta na frente do
DispatcherServlete protege por URL/request. É a primeira linha. ODispatcherServlete o pipeline MVC são assunto do Galho 9 — aqui só ancoramos que o chain vem antes dele: ver O pipeline do DispatcherServlet. - No nível de método — method security (anotações como
@PreAuthorize) protege beans diretamente, e funciona porque é implementada sobre AOP/proxies — o mesmo mecanismo de proxy de bean do Galho 8. Aqui só ancoramos a ideia; o detalhe de method security é tema de nota própria mais adiante neste galho.
A lição de iniciado: segurança no Spring não é um lugar só. Tem a cadeia na frente da web e tem a proteção dentro dos beans. As duas se complementam.
Na prática
Um SecurityFilterChain mínimo que exige autenticação em qualquer request, com HTTP Basic:
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(auth -> auth
.requestMatchers("/public/**").permitAll()
.requestMatchers("/admin/**").hasRole("ADMIN")
.anyRequest().authenticated()
)
.httpBasic(Customizer.withDefaults())
.formLogin(Customizer.withDefaults());
return http.build();
}
}Repare na separação: authorizeHttpRequests é autorização (quem pode o quê — permitAll, hasRole, authenticated), enquanto httpBasic e formLogin configuram autenticação (como você prova quem é). Mesma config, dois conceitos lado a lado, sem se confundirem.
O fluxo de uma request a um endpoint protegido (digamos GET /admin/orders, atendido por um OrderController):
Cliente (GET /admin/orders)
│
▼
FilterChain do container
│
▼
DelegatingFilterProxy ── ponte Servlet → Spring
│
▼
FilterChainProxy ── filtro central de segurança
│
│ escolhe via RequestMatcher
▼
SecurityFilterChain ── filtros ordenados:
│ 1. autenticação (Basic / form login)
│ 2. autorização (AuthorizationFilter)
│
│ request autenticado + autorizado?
▼
DispatcherServlet ── (Galho 9) pipeline MVC
│
▼
OrderController.list() ── chega o request limpoSe a autenticação falha, ou a autorização nega, o request nunca chega ao OrderController — a cadeia corta antes.
Armadilhas
(1) Misturar autenticação e autorização
São perguntas diferentes: authn é “quem é você?”, authz é “o que você pode?“. Tratar “está logado” como se fosse “pode fazer” leva a brechas — um usuário autenticado não é automaticamente autorizado para tudo.
Exemplo: deixar anyRequest().authenticated() e achar que /admin/** está protegido contra usuários comuns. Não está — qualquer usuário logado passa. Falta a regra de autorização por papel.
Fix: separe sempre as duas camadas — autentique com httpBasic/formLogin e autorize com requestMatchers(...).hasRole(...) distinto de .authenticated().
(2) Usar WebSecurityConfigurerAdapter
WebSecurityConfigurerAdapter foi removido na 6.0. Tutoriais e respostas de fórum antigos mandam estender essa classe e sobrescrever configure(HttpSecurity) — isso nem compila na 6.x.
Exemplo: public class SecurityConfig extends WebSecurityConfigurerAdapter { ... } → erro de compilação (classe inexistente).
Fix: declare um bean SecurityFilterChain recebendo HttpSecurity e retornando http.build(), numa @Configuration com @EnableWebSecurity.
(3) Achar que “Spring Security = formulário de login”
O form login é um filtro dentro da cadeia, não o framework inteiro. Reduzir Spring Security à tela de login esconde a autorização, as proteções contra exploits (CSRF, headers) e a própria mecânica do filter chain.
Exemplo: configurar só formLogin e assumir que a app está “segura”, sem nenhuma regra de autorização nem entender que cada request passa pela cadeia toda.
Fix: pense em filter chain, não em “login” — a cadeia inteira (autenticação + autorização + proteções) é o que protege a aplicação.
Em entrevista
Frase pronta (inglês)
Spring Security works as a filter chain that sits in front of the
DispatcherServlet: every request goes through aFilterChainProxy, which picks a matchingSecurityFilterChainand runs its ordered security filters before the request ever reaches a controller. The key trade-off it enforces is keeping authentication (“who are you?”) and authorization (“what are you allowed to do?”) as two separate stages — you always authenticate first, then authorize. The modern decision in 6.x is to define aSecurityFilterChainbean using the lambda DSL with@EnableWebSecurity; the important caveat is thatWebSecurityConfigurerAdapterwas removed in 6.0, so any code extending it simply won’t compile anymore.
Vocabulário
| Termo PT | Termo EN |
|---|---|
| autenticação | authentication (authn) |
| autorização | authorization (authz) |
| cadeia de filtros | filter chain |
| filtro de segurança | security filter |
| usuário autenticado | authenticated user |
| papel / função | role |
| ponto de entrada de autenticação | authentication entry point |
| acesso negado | access denied |
Veja também
- SecurityContext, Authentication e Principal
- A arquitetura do filter chain em profundidade
- Interceptors vs Filters
- O pipeline do DispatcherServlet
- Segurança (MOC do galho)
- Trilha Java
- Dicionário de Java