Capítulo 09: Spring Security 6 Architecture, SecurityFilterChain e BCrypt
Especialização em Backend com Java & Spring Boot • Spring Boot 3 & Java 21 LTS • Spring Data JPA, Security, Microsserviços e Cloud
🗺️ Mapa Conceitual do Tópico
flowchart TD
A["Cliente HTTP / Frontend"] --> B["API Gateway / Router"]
B --> C["Controller / Handler"]
C --> D["Service Layer (Regras de Negócio)"]
D --> E["Repository / ORM (Persistência)"]
E --> F["Banco de Dados / Cache"]
subgraph ARQ["Arquitetura do Capítulo"]
G["Conceito: Spring Security 6 Architecture, SecurityFilterChain e BCrypt"]
H["Segurança, Validação e Resiliência"]
I["Alta Performance e Escalabilidade"]
end
D --> ARQ
style A fill:#e1f5fe,stroke:#03a9f4,stroke-width:2px
style B fill:#fff3e0,stroke:#ff9800,stroke-width:2px
style C fill:#ede7f6,stroke:#7e57c2,stroke-width:2px
style D fill:#e8f5e9,stroke:#4caf50,stroke-width:2px
style E fill:#fce4ec,stroke:#e91e63,stroke-width:2px
style F fill:#f3e5f5,stroke:#9c27b0,stroke-width:2px
🏛️ 1. Fundamentos Técnicos de Spring Security 6 Architecture, SecurityFilterChain e BCrypt
A cadeia de filtros Servlet. O Spring Security se integra à aplicação via DelegatingFilterProxy, um único filtro Servlet registrado no container que delega para uma cadeia interna de filtros gerenciados pelo Spring (FilterChainProxy). Cada requisição HTTP passa por essa cadeia — filtros de CORS, CSRF, autenticação (UsernamePasswordAuthenticationFilter, ou um filtro JWT customizado), tratamento de exceções — antes de alcançar o DispatcherServlet e, portanto, antes de qualquer @Controller.
Fim do WebSecurityConfigurerAdapter. Até o Spring Security 5, a configuração se dava estendendo essa classe abstrata e sobrescrevendo configure(HttpSecurity). A partir do Spring Security 5.7 (e obrigatório no Spring Boot 3 / Security 6), o modelo é baseado em componentes: declara-se um @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http), configurado com uma DSL de lambdas (http.csrf(csrf -> csrf.disable())...), permitindo múltiplas SecurityFilterChain (uma por padrão de URL) coexistindo no mesmo contexto.
Statelessness em APIs REST. Para APIs consumidas por SPAs/mobile com tokens (JWT), não faz sentido manter sessão HTTP no servidor: sessionManagement(s -> s.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) instrui o Spring Security a nunca criar ou usar HttpSession. Como consequência, a proteção CSRF baseada em cookie de sessão se torna desnecessária e é comumente desabilitada nesse cenário (csrf.disable()) — mas apenas quando não há autenticação por cookie simultânea.
authorizeHttpRequests. Define, em ordem de prioridade (a primeira regra que casa com o padrão de URL prevalece), quais rotas exigem autenticação/papel específico e quais são públicas (permitAll()). Regras mais específicas devem vir antes de regras genéricas como anyRequest().authenticated().
PasswordEncoder e BCrypt. Senhas nunca devem ser armazenadas em texto puro nem com hashes rápidos genéricos (MD5/SHA-256 sozinhos), vulneráveis a brute-force com GPU. BCryptPasswordEncoder aplica um algoritmo deliberadamente lento e computacionalmente caro, com salt aleatório embutido no próprio hash de saída — cada chamada de .encode() para a mesma senha produz um hash diferente, e .matches(raw, hash) recalcula e compara. O parâmetro de strength (rounds, padrão 10) controla o custo: cada incremento dobra o tempo de processamento, uma defesa ajustável contra o avanço de hardware.
SecurityContextHolder e Authentication. Após autenticação bem-sucedida, o Spring Security popula um objeto Authentication (contendo Principal, credenciais e GrantedAuthoritys) dentro do SecurityContextHolder, por padrão armazenado em ThreadLocal — acessível de qualquer camada da aplicação durante o processamento daquela requisição via SecurityContextHolder.getContext().getAuthentication().
Tratamento de exceções de segurança. AuthenticationEntryPoint é acionado quando um usuário não autenticado tenta acessar um recurso protegido (deveria devolver 401); AccessDeniedHandler é acionado quando um usuário autenticado, mas sem a autoridade necessária, tenta acessar algo (403). Em APIs REST, ambos devem ser customizados para retornar um corpo JSON padronizado em vez do comportamento padrão de redirecionar para uma página de login HTML.
💻 2. Código de Demonstração Corporativo
package com.empresa.api.config;
import com.empresa.api.security.CustomAccessDeniedHandler;
import com.empresa.api.security.CustomAuthEntryPoint;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
@Configuration
@EnableWebSecurity
public class SecurityConfig {
private final CustomAuthEntryPoint authEntryPoint;
private final CustomAccessDeniedHandler accessDeniedHandler;
public SecurityConfig(CustomAuthEntryPoint authEntryPoint, CustomAccessDeniedHandler accessDeniedHandler) {
this.authEntryPoint = authEntryPoint;
this.accessDeniedHandler = accessDeniedHandler;
}
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
return http
.csrf(csrf -> csrf.disable()) // API stateless com JWT: sem cookies de sessão a proteger
.sessionManagement(s -> s.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.authorizeHttpRequests(auth -> auth
.requestMatchers("/api/v1/auth/**", "/actuator/health").permitAll()
.requestMatchers("/api/v1/admin/**").hasRole("ADMIN")
.anyRequest().authenticated()
)
.exceptionHandling(ex -> ex
.authenticationEntryPoint(authEntryPoint) // 401 em JSON
.accessDeniedHandler(accessDeniedHandler) // 403 em JSON
)
.build();
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder(12); // 12 rounds: custo elevado propositalmente
}
}
package com.empresa.api.security;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.stereotype.Component;
import java.io.IOException;
@Component
public class CustomAuthEntryPoint implements AuthenticationEntryPoint {
@Override
public void commence(HttpServletRequest request, HttpServletResponse response,
AuthenticationException authException) throws IOException {
response.setContentType("application/json");
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
response.getWriter().write("{\"erro\": \"Não autenticado\", \"status\": 401}");
}
}
package com.empresa.api.security;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.stereotype.Component;
import java.io.IOException;
@Component
public class CustomAccessDeniedHandler implements AccessDeniedHandler {
@Override
public void handle(HttpServletRequest request, HttpServletResponse response,
AccessDeniedException accessDeniedException) throws IOException {
response.setContentType("application/json");
response.setStatus(HttpServletResponse.SC_FORBIDDEN);
response.getWriter().write("{\"erro\": \"Acesso negado\", \"status\": 403}");
}
}
🔗 Recursos Pedagógicos do Capítulo 09
| Recurso Didático | Finalidade | Link de Acesso |
|---|---|---|
| 📊 Slides de Aula | Apresentação visual interativa com Dark Mode e suporte a teclado | Ver Slides |
| 🧠 Quiz Formativo | Teste interativo de fixação com feedback imediato por alternativa | Fazer Quiz |
| 💻 Exemplos de Código | Demonstrações funcionais com código executável | Ver Exemplos |
| 🧩 Exercícios em 4 Níveis | Lista progressiva de fixação com gabarito em bloco colapsável | Resolver Exercícios |
| ⬅️ Capítulo Anterior | 📚 Sumário de Tópicos | Próximo Capítulo ➡️ |