Spring Boot giúp tạo endpoint webhook với @RestController dễ dàng, nhưng Spring Security CSRF và JSON converter có thể chặn hoặc đổi payload trước handler. Test cục bộ cần chứng minh truy cập byte[] thô, miễn CSRF trên path webhook và xác minh chữ ký trên byte nguyên vẹn.
Tạo endpoint webhook @RestController
Giữ controller mỏng. Nhận byte thô, xác minh chữ ký, rồi deserialize:
@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[] giữ payload chính xác Tomcat nhận — lý tưởng khi provider ký JSON thô.
Đọc raw body qua HttpServletRequest
Hoặc đọc trực tiếp từ servlet request:
byte[] body = request.getInputStream().readAllBytes();
String sig = request.getHeader("Stripe-Signature");
Đừng bind POJO hay Map trước — Jackson reserialize phá HMAC. Xem hướng dẫn xác minh chữ ký.
Tắt CSRF chỉ cho path webhook
Spring Security bật CSRF mặc định. POST webhook lỗi 403 nếu không miễ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();
}
}
Giới hạn ignore ở /webhooks/** — không tắt CSRF toàn app.
Xác minh header chữ ký trước logic nghiệp vụ
Đọc header provider, tính digest từ mảng byte thô, so sánh constant-time. 401 nếu sai, 200 nhanh khi đúng.
Quy trình tunnel cục bộ
- Chạy:
./mvnw spring-boot:runcổng 8080. - Expose Tomcat:
npx portpreview 8080. - Dán URL tunnel + path vào dashboard.
- Gửi sự kiện thử và xem log.
- Phát lại cùng event ID để idempotency — xem mẫu retry.
Bẫy thường gặp
Thứ tự filter và tiêu thụ body
Filter đọc stream trước controller để stream rỗng. Dùng ContentCachingRequestWrapper nếu cần.
Binding @RequestBody toàn cục
@ControllerAdvice parse JSON sớm có thể đổi byte. Tách webhook với byte[].
Handler chậm gây retry
Xác nhận nhanh, xử lý async, dedupe theo event ID.
Đọc thêm
Đọc thêm về cơ bản về localhost tunneling và gỡ lỗi webhook cục bộ. Cơ chế chữ ký xem hướng dẫn xác minh chữ ký. Handler chống trùng lặp xem mẫu retry và idempotency. bắt đầu PortPreview miễn phí.
![RestController Spring Boot trên embedded Tomcat xác minh header chữ ký webhook từ byte[] thô nhận qua tunnel cục bộ.](/_next/image?url=%2Fimages%2Farticles%2Fspring-boot-webhook-local-testing%2Fcover.png&w=3840&q=75)