Spring Boot facilita endpoints webhook con @RestController, pero Spring Security CSRF y los convertidores JSON pueden bloquear o mutar payloads antes del handler. Las pruebas locales deben demostrar acceso byte[] crudo, exenciones CSRF en rutas webhook y verificación de firma sobre bytes intactos.
Crear un endpoint webhook @RestController
Mantén controladores webhook ligeros. Acepta bytes crudos, verifica firma, luego deserializa:
@RestController
@RequestMapping("/webhooks")
public class StripeWebhookController {
@PostMapping("/stripe")
public ResponseEntity handle(
@RequestBody byte[] payload,
@RequestHeader("Stripe-Signature") String signature) {
if (!verifier.isValid(payload, signature)) {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
}
JsonNode event = objectMapper.readTree(payload);
handler.process(event);
return ResponseEntity.ok().build();
}
}
@RequestBody byte[] preserva el payload exacto que Tomcat recibió — ideal cuando el proveedor firma JSON crudo.
Leer cuerpo crudo con HttpServletRequest
Alternativamente, lee directamente de la petición servlet:
byte[] body = request.getInputStream().readAllBytes();
String sig = request.getHeader("Stripe-Signature");
Evita enlazar a un POJO o Map primero — la reserialización Jackson rompe HMAC. Ver guía de verificación de firmas.
Desactivar CSRF solo en rutas webhook
Spring Security habilita CSRF por defecto. Los POST webhook fallan con 403 sin exención server-to-server:
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.csrf(csrf -> csrf.ignoringRequestMatchers("/webhooks/**"));
return http.build();
}
}
Limita el ignore a /webhooks/** — no desactives CSRF globalmente.
Verificar cabeceras de firma antes de la lógica de negocio
Lee cabeceras del proveedor, calcula el digest del array de bytes crudo y compara en tiempo constante. 401 si falla, 200 rápido para reducir reintentos.
Flujo de túnel local
- Inicia:
./mvnw spring-boot:runen puerto 8080. - Expón Tomcat:
npx portpreview 8080. - Pega URL del túnel + ruta en el dashboard.
- Envía evento de prueba y revisa logs.
- Repite el mismo event ID para idempotencia — ver patrones de reintentos.
Trampas comunes
Orden de filtros y consumo del body
Filtros que leen el stream antes del controlador lo dejan vacío. Usa ContentCachingRequestWrapper si varias capas inspeccionan el body.
Binding global @RequestBody
Un @ControllerAdvice que parsea JSON temprano puede alterar bytes. Aísla endpoints webhook con binding byte[].
Handlers lentos provocan reintentos
Confirma rápido, procesa async, deduplica por event ID.
Para profundizar
Para los fundamentos, lea los fundamentos del tunneling localhost y depuración práctica de webhooks en local. Para la criptografía, consulte la guía de verificación de firmas. Para handlers sin duplicados, lea patrones de reintentos e idempotencia. empiece PortPreview gratis.
![Un RestController Spring Boot en Tomcat embebido verifica cabeceras de firma webhook desde payload byte[] crudo recibido por un túnel local.](/_next/image?url=%2Fimages%2Farticles%2Fspring-boot-webhook-local-testing%2Fcover.png&w=3840&q=75)