Spring Boot CAPTCHA Integration
This recipe shows how to integrate TrustCaptcha into a Spring Boot application. The frontend setup is the same as for any other JVM application — this page focuses on the server-side validation.
The setup section gets you to a working integration in three small steps using a controller method directly. Below it, an optional refactor section shows the more reusable Spring-Boot-idiomatic approach (Bean Validation annotation).
Preparation
Section titled “Preparation”You should have already completed the following steps before you wire TrustCaptcha into your Spring Boot application.
Read Get-Started: Get a quick overview of the concepts behind TrustCaptcha and the integration process in get started.
Existing CAPTCHA: If you don’t have a CAPTCHA yet, sign in or create a new user account. Then create a new CAPTCHA.
1. Embed the frontend widget
Section titled “1. Embed the frontend widget”First, add the TrustCaptcha script to your page (see the JavaScript Guide for version pinning and self-hosting options).
Then place the <trustcaptcha-component> element inside your Thymeleaf form. The widget appends a hidden tc-verification-token field on submit, which your Spring Boot controller receives like any other form input.
<script type="module" src="https://cdn.trustcomponent.com/trustcaptcha/3.0.x/trustcaptcha.esm.min.js"></script>
<form th:action="@{/contact}" method="post"> <label>Email</label> <input type="email" name="email" required>
<trustcaptcha-component sitekey="<your_site_key>"></trustcaptcha-component>
<button type="submit">Send</button></form>See the Widget Overview for the full property reference.
2. Install the JVM SDK
Section titled “2. Install the JVM SDK”<dependency> <groupId>com.trustcomponent</groupId> <artifactId>trustcaptcha</artifactId> <version>3.0.0</version></dependency>3. Validate the token in your controller
Section titled “3. Validate the token in your controller”package com.example.contact;
import com.trustcomponent.trustcaptcha.TrustCaptcha;import com.trustcomponent.trustcaptcha.exception.CaptchaFailureException;import com.trustcomponent.trustcaptcha.model.VerificationResult;import org.springframework.stereotype.Controller;import org.springframework.ui.Model;import org.springframework.web.bind.annotation.PostMapping;import org.springframework.web.bind.annotation.RequestParam;
@Controllerpublic class ContactController {
@PostMapping("/contact") public String submit(@RequestParam("tc-verification-token") String token, Model model) {
// In production, load from application.yml: @Value("${trustcaptcha.api-key}") String apiKey = "<your_api_key>";
VerificationResult result; try { result = TrustCaptcha.getVerificationResult(apiKey, token); } catch (CaptchaFailureException e) { model.addAttribute("error", "CAPTCHA verification failed."); return "contact"; }
if (!result.isVerificationPassed() || result.getScore() > 0.5) { model.addAttribute("error", "CAPTCHA verification failed."); return "contact"; }
// CAPTCHA passed — request data is safe to use. // ... your business logic ...
return "contact-success"; }}That’s it — the form is now protected. For real deployments, move the API key out of the source code (see the comment) and consider explicit failover handling — see Failover Behavior for the reasoning and a code template.
Refactor: extract to a Bean Validation annotation
Section titled “Refactor: extract to a Bean Validation annotation”If you protect more than one endpoint, the most idiomatic Spring Boot approach is a custom Bean Validation annotation. The verification call then runs automatically whenever you mark a controller parameter with @Valid.
Configure the API key
Section titled “Configure the API key”trustcaptcha: api-key: ${TRUSTCAPTCHA_API_KEY}Make sure spring-boot-starter-validation is on your classpath (it is by default with spring-boot-starter-web):
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-validation</artifactId></dependency>Create the annotation and validator
Section titled “Create the annotation and validator”package com.example.captcha;
import jakarta.validation.Constraint;import jakarta.validation.Payload;import java.lang.annotation.*;
@Documented@Constraint(validatedBy = TrustCaptchaTokenValidator.class)@Target({ElementType.FIELD, ElementType.PARAMETER})@Retention(RetentionPolicy.RUNTIME)public @interface TrustCaptchaToken { String message() default "CAPTCHA verification failed."; Class<?>[] groups() default {}; Class<? extends Payload>[] payload() default {};}package com.example.captcha;
import com.trustcomponent.trustcaptcha.TrustCaptcha;import com.trustcomponent.trustcaptcha.exception.CaptchaFailureException;import com.trustcomponent.trustcaptcha.model.VerificationResult;import jakarta.validation.ConstraintValidator;import jakarta.validation.ConstraintValidatorContext;import org.springframework.beans.factory.annotation.Value;import org.springframework.stereotype.Component;
@Componentpublic class TrustCaptchaTokenValidator implements ConstraintValidator<TrustCaptchaToken, String> {
@Value("${trustcaptcha.api-key}") private String apiKey;
@Override public boolean isValid(String value, ConstraintValidatorContext context) { if (value == null || value.isBlank()) return false; try { VerificationResult result = TrustCaptcha.getVerificationResult(apiKey, value); return result.isVerificationPassed() && result.getScore() <= 0.5; } catch (CaptchaFailureException e) { return false; } }}Use the annotation on a DTO
Section titled “Use the annotation on a DTO”Java identifiers can’t contain dashes, so the DTO field is tcVerificationToken while the widget posts tc-verification-token. The simplest fix is to align the names by setting token-field-name="tcVerificationToken" on the widget — Spring’s data binder then maps the form field to the DTO property automatically.
package com.example.contact;
import com.example.captcha.TrustCaptchaToken;import jakarta.validation.constraints.Email;import jakarta.validation.constraints.NotBlank;
public class ContactForm {
@NotBlank @Email private String email;
@TrustCaptchaToken private String tcVerificationToken;
// getters and setters}Mark the controller parameter with @Valid — Spring runs every constraint, including @TrustCaptchaToken, automatically:
@PostMapping("/contact")public String submit(@Valid @ModelAttribute ContactForm form, BindingResult bindingResult) { if (bindingResult.hasErrors()) return "contact"; // CAPTCHA already validated return "contact-success";}For @RequestBody JSON endpoints, replace @ModelAttribute accordingly and add @JsonProperty("tc-verification-token") on the field if you’d rather keep the dashed JSON key while using a Java-friendly property name.
Spring Security. TrustCaptcha sits at the application layer (@Valid on the controller / DTO), independent of Spring Security. Both can stay enabled — the CAPTCHA does not replace authentication or CSRF protection.
WebFlux / reactive. The JVM SDK is blocking. In a reactive controller, run the validation on a bounded elastic scheduler (Mono.fromCallable(() -> ...).subscribeOn(Schedulers.boundedElastic())) to avoid blocking the event loop.
Singleton SDK instance. For configured usage (custom timeouts, proxy, custom API host), expose a single TrustCaptcha bean built once at startup and inject it into the validator instead of using the static shortcut. See the JVM Guide for the builder API.
Next steps
Section titled “Next steps”Once you have wired TrustCaptcha into your Spring Boot application, you can use TrustCaptcha to its full extent. However, we still recommend the following additional technical and organizational measures:
Security rules: You can find many security settings for your CAPTCHA in the CAPTCHA settings. These include, for example, authorized websites, CAPTCHA bypass for specific IP addresses, bypass keys, IP based blocking, geoblocking, individual difficulty and duration of the CAPTCHA, and much more. Learn more about the security rules.
Privacy & GDPR compliance: Include a passage in your privacy policy that refers to the use of TrustCaptcha. We also recommend that you enter into a data processing agreement with us to stay GDPR-compliant. Learn more about data protection.
Accessibility & UX: Customize TrustCaptcha to your website so that your website is as accessible as possible and offers the best possible user experience. More about accessibility.
Failover behavior: Decide how your backend should behave when our service is temporarily unreachable. This is particularly important for high-availability flows where blocking real users during an outage is worse than letting through a small amount of unverified traffic. Learn more about failover behavior.
Testing: If you use automated testing, make sure that the CAPTCHA does not block it. Learn more about testing.