Tất cả bài viết
RestController Spring Boot trên embedded Tomcat xác minh header chữ ký webhook từ byte[] thô nhận qua tunnel cục bộ.
Spring BootJavawebhook debugginglocal testing

Webhook Spring Boot: raw body, miễn CSRF và header chữ ký

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ộ

  1. Chạy: ./mvnw spring-boot:run cổng 8080.
  2. Expose Tomcat: npx portpreview 8080.
  3. Dán URL tunnel + path vào dashboard.
  4. Gửi sự kiện thử và xem log.
  5. 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 tunnelinggỡ 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í.

Câu hỏi thường gặp

Tại sao webhook Spring Boot trả 403?
CSRF Spring Security chặn POST không có session token. Thêm csrf.ignoringRequestMatchers cho path webhook rồi xác minh chữ ký.
Dùng byte[] hay POJO cho webhook?
Dùng byte[] để xác minh chữ ký. POJO binding reserialize JSON phá HMAC.
Làm sao test webhook Spring Boot cục bộ?
Chạy spring-boot:run cổng 8080, expose bằng npx portpreview, dán URL tunnel và kích hoạt sự kiện thử.