💻 Exemplos: Capítulo 10: Autenticação Stateless com JWT e Filtros Customizados
Especialização em Backend com Java & Spring Boot • Demonstrações Práticas Compiláveis
💻 Código de Exemplo 01 — Endpoint de Login com AuthenticationManager e Emissão de Token
package com.empresa.api.controller;
import com.empresa.api.security.TokenService;
import org.springframework.http.ResponseEntity;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/api/v1/auth")
public class AuthController {
record LoginRequest(String email, String senha) {}
record TokenResponse(String accessToken, String tipo, long expiraEmSegundos) {}
private final AuthenticationManager authenticationManager;
private final TokenService tokenService;
public AuthController(AuthenticationManager authenticationManager, TokenService tokenService) {
this.authenticationManager = authenticationManager;
this.tokenService = tokenService;
}
@PostMapping("/login")
public ResponseEntity<TokenResponse> login(@RequestBody LoginRequest request) {
// AuthenticationManager delega a um DaoAuthenticationProvider que consulta
// UserDetailsService e compara a senha com PasswordEncoder.matches(...).
Authentication authentication = authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(request.email(), request.senha()));
List<String> papeis = authentication.getAuthorities().stream()
.map(GrantedAuthority::getAuthority)
.toList();
String token = tokenService.gerarToken(authentication.getName(), papeis);
return ResponseEntity.ok(new TokenResponse(token, "Bearer", 900));
}
}
💻 Código de Exemplo 02 — Registro do Filtro JWT na SecurityFilterChain
package com.empresa.api.config;
import com.empresa.api.security.JwtAuthenticationFilter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.web.SecurityFilterChain;
@Configuration
public class SecurityConfig {
private final JwtAuthenticationFilter jwtAuthenticationFilter;
public SecurityConfig(JwtAuthenticationFilter jwtAuthenticationFilter) {
this.jwtAuthenticationFilter = jwtAuthenticationFilter;
}
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
return http
.csrf(csrf -> csrf.disable())
.sessionManagement(s -> s.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.authorizeHttpRequests(auth -> auth
.requestMatchers("/api/v1/auth/**").permitAll()
.anyRequest().authenticated()
)
// O filtro JWT roda ANTES do filtro padrão de usuário/senha do Spring Security.
.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class)
.build();
}
}
💻 Código de Exemplo 03 — Refresh Token com Rotação e Persistência em Redis
package com.empresa.api.security;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import java.time.Duration;
import java.util.UUID;
@Service
public class RefreshTokenService {
private static final Duration TTL = Duration.ofDays(7);
private final StringRedisTemplate redisTemplate;
public RefreshTokenService(StringRedisTemplate redisTemplate) {
this.redisTemplate = redisTemplate;
}
public String emitir(String usuario) {
String jti = UUID.randomUUID().toString();
redisTemplate.opsForValue().set("refresh:" + jti, usuario, TTL);
return jti;
}
/**
* Rotação: o refresh token usado é imediatamente descartado e um novo é emitido.
* Se o mesmo jti for apresentado de novo, ele já não existirá na chave Redis
* (retorna null) — sinal de possível roubo/reuso do token.
*/
public String rotacionar(String jtiAntigo) {
String usuario = redisTemplate.opsForValue().get("refresh:" + jtiAntigo);
if (usuario == null) {
throw new IllegalStateException("Refresh token inválido, expirado ou já utilizado");
}
redisTemplate.delete("refresh:" + jtiAntigo);
return emitir(usuario);
}
public void revogar(String jti) {
redisTemplate.delete("refresh:" + jti);
}
}