404 Not Found

404 Not Found


nginx

Fundamentos do Spring Security

O Spring Security é o padrão da indústria para segurança Java—com seu trio de cadeia de filtros, autenticação e autorização—ele protege a segurança da API.

1. O Que Você Vai Aprender


2. Uma História Real de um Gerente de Produto

(1) Ponto de Dor: APIs Desprotegidas

Durante uma reunião de revisão de produto, Charlie descobriu que nenhuma das APIs do OrderFlow tinha autenticação—qualquer pessoa poderia chamar as APIs para criar pedidos ou excluir produtos. Para piorar, Bob havia exposto o endpoint Actuator na internet pública, permitindo que scanners obtivessem informações do banco de dados de produção. Charlie exigiu que controles de segurança fossem implementados imediatamente, e Alice precisava da solução mais rápida possível.

(2) Solução do Spring Security

O Spring Security pode proteger sua API com apenas algumas linhas de configuração:

JAVA
@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http.csrf(csrf -> csrf.disable())
            .authorizeHttpRequests(auth -> auth
                .requestMatchers("/api/v1/admin/**").hasRole("ADMIN")
                .requestMatchers("/api/v1/orders/**").authenticated()
                .anyRequest().permitAll()
            )
            .httpBasic(Customizer.withDefaults());
        return http.build();
    }
}

(3) Resultado

Depois que Alice implementou autenticação e autorização para o OrderFlow usando Spring Security, a API de gerenciamento ficou acessível apenas para usuários com o papel ADMIN, a API padrão exigia que os usuários fizessem login, e endpoints Actuator foram restritos ao acesso da rede interna. Charlie ficou satisfeito com a conformidade de segurança.


3. Arquitetura do Spring Security

(1) Cadeia de Filtros

100%
sequenceDiagram
    participant Client
    participant Chain as SecurityFilterChain
    participant Auth as Authentication Filter
    participant Authz as Authorization Filter
    participant Controller

    Client->>Chain: HTTP Request
    Chain->>Auth: 1. Authentication
    alt Credenciais Inválidas
        Auth-->>Client: 401 Unauthorized
    end
    Auth->>Authz: 2. Authorization
    alt Acesso Negado
        Authz-->>Client: 403 Forbidden
    end
    Authz->>Controller: 3. Forward to Controller
    Controller-->>Client: 200 OK
Conceitos-Chave Descrição
SecurityFilterChain Uma cadeia ordenada de filtros, através da qual cada requisição passa sequencialmente
Authentication Verificação: Confirmar "Quem Você É"
Authorization Autorização: Confirmar "O Que Você Pode Fazer"
SecurityContext Contexto de segurança contendo informações do usuário atual
GrantedAuthority Informações de Permissão/Papel

(2) Relações Entre Componentes Principais

Componente Responsabilidades Interface
AuthenticationManager Gerenciador de Autenticação authenticate()
ProviderManager Implementação padrão do AuthenticationManager Delega para AuthenticationProvider
UserDetailsService Carregar informações do usuário loadUserByUsername()
PasswordEncoder Codificação de Senha encode() / matches()

4. Configuração do SecurityFilterChain

(1) ▶ Exemplo: Configuração Básica de Segurança

JAVA
@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
            .csrf(csrf -> csrf.disable())
            .cors(cors -> cors.configurationSource(corsConfigurationSource()))
            .sessionManagement(session ->
                session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
            .authorizeHttpRequests(auth -> auth
                .requestMatchers("/api/v1/products/**").permitAll()
                .requestMatchers("/api/v1/orders/**").authenticated()
                .requestMatchers("/api/v1/admin/**").hasRole("ADMIN")
                .anyRequest().authenticated()
            )
            .httpBasic(Customizer.withDefaults());
        return http.build();
    }

    @Bean
    public CorsConfigurationSource corsConfigurationSource() {
        CorsConfiguration config = new CorsConfiguration();
        config.setAllowedOrigins(List.of("https://orderflow.example.com"));
        config.setAllowedMethods(List.of("GET", "POST", "PUT", "DELETE"));
        config.setAllowedHeaders(List.of("*"));
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/api/**", config);
        return source;
    }
}

Saída:

TEXT
// Execução bem-sucedida
Opção de Configuração Descrição Recomendação para API REST
CSRF Proteção contra Falsificação de Requisição Cross-Site Desabilitado (não necessário para APIs stateless)
CORS Compartilhamento de Recursos entre Origens Configurar Domínios Permitidos
Session Gerenciamento de Sessão STATELESS (Sem estado)
httpBasic Autenticação HTTP Basic Para Desenvolvimento/Teste
formLogin Login por Formulário Para aplicações web tradicionais

5. Autenticação de Usuário Em Memória

(1) ▶ Exemplo: InMemoryUserDetailsManager

JAVA
@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Bean
    public UserDetailsService userDetailsService(PasswordEncoder encoder) {
        UserDetails admin = User.builder()
            .username("alice")
            .password(encoder.encode("admin123"))
            .roles("ADMIN", "CUSTOMER")
            .build();

        UserDetails customer = User.builder()
            .username("bob")
            .password(encoder.encode("customer123"))
            .roles("CUSTOMER")
            .build();

        return new InMemoryUserDetailsManager(admin, customer);
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}

Saída:

TEXT
// Execução bem-sucedida
PasswordEncoder Segurança Casos de Uso
BCryptPasswordEncoder Alta (inclui valor salt) Recomendado para ambientes de produção
Argon2PasswordEncoder Mais Alta (Resistência a GPU) Requisitos de Alta Segurança
NoOpPasswordEncoder Nenhuma (texto puro) Apenas para fins de teste
🔒 Segurança: Nunca use NoOpPasswordEncoder em ambiente de produção. BCrypt é o padrão mínimo; Argon2 é um padrão mais alto.

(2) ▶ Exemplo: Teste de Certificação

BASH
# Acesso sem credenciais -> 401
curl http://localhost:8080/api/v1/orders

# Acesso com credenciais de cliente -> 200
curl -u bob:customer123 http://localhost:8080/api/v1/orders

# Acesso ao endpoint admin com cliente -> 403
curl -u bob:customer123 http://localhost:8080/api/v1/admin/dashboard

# Acesso ao endpoint admin com admin -> 200
curl -u alice:admin123 http://localhost:8080/api/v1/admin/dashboard

Saída:

TEXT
{"status":"ok","data":{}}

6. Autorização em Nível de Método

(1) ▶ Exemplo: Autorização de Método com @PreAuthorize

JAVA
@Service
public class OrderService {

    @Transactional(readOnly = true)
    @PreAuthorize("hasAnyRole('ADMIN', 'CUSTOMER')")
    public Order getOrder(Long orderId) {
        return orderRepository.findById(orderId).orElseThrow();
    }

    @Transactional
    @PreAuthorize("hasRole('ADMIN')")
    public void deleteOrder(Long orderId) {
        orderRepository.deleteById(orderId);
    }

    @Transactional
    @PreAuthorize("hasRole('ADMIN') or #customerId == authentication.principal.id")
    public List<Order> getCustomerOrders(Long customerId) {
        return orderRepository.findByCustomerId(customerId);
    }
}

Saída:

TEXT
// Execução bem-sucedida
Anotações Recursos Casos de Uso
@PreAuthorize Expressão SpEL, verifica antes de acessar o método Mais flexível, recomendado
@PostAuthorize Expressão SpEL; verifica após o método ser executado Permissões devem ser determinadas com base no valor de retorno
@Secured Lista de papéis, não suporta SpEL Verificação simples de papel
@RolesAllowed Anotações Padrão JSR-250 Compatibilidade entre frameworks
📌 Ponto-Chave: Para usar autorização em nível de método, adicione @EnableMethodSecurity à classe de configuração.

(2) ▶ Exemplo: Configuração @EnableMethodSecurity

JAVA
@Configuration
@EnableWebSecurity
@EnableMethodSecurity
public class SecurityConfig {
    // ... SecurityFilterChain e beans UserDetailsService
}

Saída:

TEXT
// Execução bem-sucedida

7. Referência Rápida de Regras de Autorização de Caminho

Método de Correspondência Descrição Exemplo
requestMatchers(String) Correspondência de caminho estilo Ant /api/v1/orders/**
requestMatchers(HttpMethod, String) Métodos HTTP Restritos POST /api/v1/orders
anyRequest() Corresponde a todas as requisições Colocado por último como catch-all
Método de Autorização Descrição
permitAll() Aberto a todos
authenticated() Autenticação Necessária
hasRole("ADMIN") Requer o papel ADMIN
hasAnyRole("A", "B") Qualquer papel necessário
denyAll() Bloquear Todo Acesso

(1) ▶ Exemplo: Restringindo Acesso à API por Papel

JAVA
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
    http.csrf(csrf -> csrf.disable())
        .authorizeHttpRequests(auth -> auth
            .requestMatchers(HttpMethod.GET, "/api/v1/products/**").permitAll()
            .requestMatchers(HttpMethod.POST, "/api/v1/products").hasRole("ADMIN")
            .requestMatchers(HttpMethod.PUT, "/api/v1/products/**").hasRole("ADMIN")
            .requestMatchers(HttpMethod.DELETE, "/api/v1/products/**").hasRole("ADMIN")
            .requestMatchers("/api/v1/orders/**").hasAnyRole("ADMIN", "CUSTOMER")
            .requestMatchers("/actuator/**").hasRole("ADMIN")
            .anyRequest().authenticated()
        )
        .httpBasic(Customizer.withDefaults());
    return http.build();
}

Saída:

TEXT
// Execução bem-sucedida

8. Exemplo Abrangente: Configuração de Segurança do OrderFlow

JAVA
// SecurityConfig.java
package com.orderflow.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
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.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.SecurityFilterChain;

@Configuration
@EnableWebSecurity
@EnableMethodSecurity
public class SecurityConfig {

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
            .csrf(csrf -> csrf.disable())
            .sessionManagement(s -> s.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
            .authorizeHttpRequests(auth -> auth
                .requestMatchers(HttpMethod.GET, "/api/v1/products/**").permitAll()
                .requestMatchers("/api/v1/orders/**").hasAnyRole("CUSTOMER", "ADMIN")
                .requestMatchers("/api/v1/admin/**").hasRole("ADMIN")
                .requestMatchers("/actuator/health").permitAll()
                .requestMatchers("/actuator/**").hasRole("ADMIN")
                .anyRequest().authenticated()
            )
            .httpBasic(Customizer.withDefaults());
        return http.build();
    }

    @Bean
    public UserDetailsService userDetailsService(PasswordEncoder encoder) {
        var admin = User.builder()
            .username("alice").password(encoder.encode("admin123"))
            .roles("ADMIN", "CUSTOMER").build();
        var customer = User.builder()
            .username("bob").password(encoder.encode("pass123"))
            .roles("CUSTOMER").build();
        return new InMemoryUserDetailsManager(admin, customer);
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}

// OrderController com segurança em nível de método
@RestController
@RequestMapping("/api/v1/orders")
public class OrderController {

    @GetMapping
    @PreAuthorize("hasAnyRole('CUSTOMER', 'ADMIN')")
    public List<Order> listOrders() { /* ... */ }

    @GetMapping("/{id}")
    @PreAuthorize("hasRole('ADMIN') or @orderSecurity.isOwner(#id, authentication)")
    public Order getOrder(@PathVariable Long id) { /* ... */ }

    @DeleteMapping("/{id}")
    @PreAuthorize("hasRole('ADMIN')")
    public void deleteOrder(@PathVariable Long id) { /* ... */ }
}

❓ Perguntas Frequentes

P Por que o CSRF deve ser desabilitado para APIs REST?
R A proteção CSRF depende de cookies e sessões. APIs REST são tipicamente stateless (usando autenticação baseada em token) e não usam cookies, então ataques CSRF não podem ter sucesso; portanto, a proteção CSRF pode ser desabilitada.
P Qual é a diferença entre hasRole e hasAuthority?
R hasRole("ADMIN") adiciona automaticamente o prefixo "ROLE_" para verificar a permissão ROLE_ADMIN. hasAuthority("ADMIN") não adiciona prefixo e verifica diretamente a permissão ADMIN. Recomendamos usar hasRole de forma consistente.
P InMemoryUserDetailsManager pode ser usado em produção?
R Não. O gerenciamento de usuários em memória é destinado apenas a desenvolvimento e teste. Em ambiente de produção, você precisa usar um UserDetailsService personalizado para carregar informações do usuário a partir de um banco de dados.
P Como obter as informações do usuário atualmente logado?
R Existem três maneiras: 1) SecurityContextHolder.getContext().getAuthentication(); 2) Injetando parâmetros em um método Controller Principal principal; 3) @AuthenticationPrincipal UserDetails user. Recomendamos o terceiro método.
P A ordem dos filtros do Spring Security pode ser personalizada?
R Sim, você pode controlar a ordem do SecurityFilterChain usando a anotação @Order. Múltiplos SecurityFilterChains podem ser configurados para corresponder a diferentes caminhos de requisição.
P Qual devo escolher, httpBasic ou formLogin?
R Use httpBasic ou Bearer Token para APIs REST; use formLogin para aplicações web tradicionais. Esta aula usa httpBasic como introdução; aulas subsequentes mudarão para JWT.

📖 Resumo


📝 Exercícios

  1. Exercício Básico (Dificuldade: ⭐): Configure Spring Security para o OrderFlow para implementar permitAll para consultas de produtos, authenticated para operações de pedidos, e hasRole("ADMIN") para APIs administrativas. Teste usando autenticação HTTP Basic e usuários em memória.

  2. Exercício Avançado (Dificuldade ⭐⭐): Implemente autorização em nível de método com @PreAuthorize—usuários só podem ver seus próprios pedidos, enquanto ADMIN pode ver todos os pedidos. Dica: Crie um bean auxiliar OrderSecurity e referencie-o no SpEL.

  3. Desafio (Dificuldade: ⭐⭐⭐): Implemente um UserDetailsService personalizado para carregar informações de usuário e papel do banco de dados (JPA), substituindo o InMemoryUserDetailsManager. Considere o design de segurança para armazenamento de senhas e o processo de registro de usuário.

Web-Tutorial.com

Equipe Técnica Web-Tutorial

Uma plataforma de tutoriais mantida por diversos desenvolvedores. Cada tutorial é escrito e revisado por profissionais da área correspondente. Trabalhamos para manter nosso conteúdo preciso e confiável — se encontrar algum problema, avise-nos.

100%