Spring Boot ułatwia endpointy webhook przez @RestController, ale Spring Security CSRF i konwertery JSON mogą blokować lub zmieniać payload przed handlerem. Test lokalny powinien potwierdzić dostęp do byte[], wyjątki CSRF na ścieżkach webhook i weryfikację podpisu na nietkniętych bajtach.
Utwórz endpoint webhook @RestController
Trzymaj kontrolery webhook lekkie. Przyjmuj surowe bajty, weryfikuj podpis, potem deserializuj:
@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[] zachowuje dokładny payload od Tomcat — idealne gdy provider podpisuje surowy JSON.
Czytaj surowe body przez HttpServletRequest
Alternatywnie czytaj bezpośnio z żądania servlet:
byte[] body = request.getInputStream().readAllBytes();
String sig = request.getHeader("Stripe-Signature");
Nie binduj najpierw do POJO ani Map — reserializacja Jackson psuje HMAC. Zobacz przewodnik weryfikacji podpisu.
Wyłącz CSRF tylko dla ścieżek webhook
Spring Security domyślnie włącza CSRF. POST webhook kończy się 403 bez wyjątku server-to-server:
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.csrf(csrf -> csrf.ignoringRequestMatchers("/webhooks/**"));
return http.build();
}
}
Ogranicz ignore do /webhooks/** — nie wyłączaj CSRF globalnie.
Weryfikuj nagłówki podpisu przed logiką biznesową
Czytaj nagłówki providera, oblicz digest z tablicy bajtów i porównuj w stałym czasie. 401 przy błędzie, szybkie 200 przy sukcesie.
Lokalny workflow tunelu
- Uruchom:
./mvnw spring-boot:runna porcie 8080. - Wystaw Tomcat:
npx portpreview 8080. - Wklej URL tunelu + ścieżkę w dashboardzie.
- Wyślij zdarzenie testowe i obserwuj logi.
- Powtórz event ID dla idempotencji — zobacz wzorce retry.
Typowe pułapki
Kolejność filtrów i konsumpcja body
Filtry czytające stream przed kontrolerem zostawiają pusty strumień. Użyj ContentCachingRequestWrapper.
Globalne wiązanie @RequestBody
Globalny @ControllerAdvice z wczesnym parsowaniem JSON może zmienić bajty. Izoluj webhook z byte[].
Wolne handlery wywołują retry
Szybko potwierdzaj, przetwarzaj async, deduplikuj po event ID.
Więcej
Podstawy znajdziesz w podstawach tunelowania localhost oraz praktycznym lokalnym debugowaniu webhooków. Mechanika podpisu — w przewodniku weryfikacji podpisu. Obsługa bez duplikatów — w wzorach retry i idempotencji. zacznij PortPreview za darmo.
![RestController Spring Boot na embedded Tomcat weryfikuje nagłówki podpisu webhook z surowego byte[] payload przez lokalny tunel.](/_next/image?url=%2Fimages%2Farticles%2Fspring-boot-webhook-local-testing%2Fcover.png&w=3840&q=75)