Усі статті
RestController Spring Boot на embedded Tomcat перевіряє заголовки підпису вебхука з сирого byte[] payload через локальний тунель.
Spring BootJavawebhook debugginglocal testing

Вебхуки Spring Boot: сирий body, виключення CSRF і заголовки підпису

Spring Boot спрощує webhook-ендпоінти через @RestController, але Spring Security CSRF і JSON-конвертери можуть блокувати або змінювати payload до handler. Локальне тестування має підтвердити доступ до byte[], виключення CSRF для webhook-шляхів і перевірку підпису по незмінених байтах.

Створіть webhook-ендпоінт @RestController

Тримайте контролери тонкими. Приймайте сирі байти, перевіряйте підпис, потім десеріалізуйте:

@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[] зберігає точний payload від Tomcat — ідеально коли провайдер підписує сирий JSON.

Читати сирий body через HttpServletRequest

Альтернатива — читати безпосередньо з servlet-запиту:

byte[] body = request.getInputStream().readAllBytes();
String sig = request.getHeader("Stripe-Signature");

Не біндіть спочатку до POJO чи Map — reserializacja Jackson ламає HMAC. Див. посібник перевірки підпису.

Вимкнути CSRF лише для webhook-шляхів

Spring Security увімкнює CSRF за замовчуванням. Webhook POST падають з 403 без виключення server-to-server:

@Configuration
@EnableWebSecurity
public class SecurityConfig {
    @Bean
    SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http.csrf(csrf -> csrf.ignoringRequestMatchers("/webhooks/**"));
        return http.build();
    }
}

Обмежте ignore патерном /webhooks/** — не вимикайте CSRF глобально.

Перевіряти заголовки підпису до бізнес-логіки

Читайте заголовки провайдера, обчислюйте digest з byte[] і порівнюйте за константний час. 401 при помилці, швидкий 200 при успіху.

Локальний workflow з тунелем

  1. Запуск: ./mvnw spring-boot:run на порту 8080.
  2. Відкрийте Tomcat: npx portpreview 8080.
  3. Вставте URL тунnelю + шлях у dashboard.
  4. Надішліть тестову подію та дивіться логи.
  5. Повторіть event ID для ідемпотентності — див. патерни повторів.

Типові пастки

Порядок фільтрів і споживання body

Фільтри, що читають stream до контролера, залишають порожній потік. Використовуйте ContentCachingRequestWrapper.

Глобальне @RequestBody binding

Глобальний @ControllerAdvice з раннім JSON-парсингом змінює байти. Ізолюйте webhook з byte[].

Повільні handler викликають повтори

Швидко підтверджуйте, обробляйте async, дедуплікуйте за event ID.

Далі

Основи — у основах тунелювання localhost та практичному локальному налагодженні вебхуків. Механіка підписів — у посібнику з перевірки підпису. Обробники без дублікатів — у патернах повторів та ідемпотентності. почніть PortPreview безкоштовно.

Поширені запитання

Чому webhook Spring Boot повертає 403?
CSRF Spring Security блокує POST без session token. Додайте csrf.ignoringRequestMatchers для webhook-шляху і перевіряйте підпис.
byte[] чи POJO для вебхуків?
Використовуйте byte[] для перевірки підпису. POJO binding reserializuje JSON і ламає HMAC.
Як тестувати Spring Boot вебхуки локально?
Запустіть spring-boot:run на 8080, відкрийте через npx portpreview, вставте URL тунnelю та надішліть тестові події.