Java
For Java, there are four clients. Three are made from the same OpenAPI description. java-resttemplate-boot4 is an adapted version of java-resttemplate. All four call the same endpoint. They differ in the Spring technology that they use.
Select the client
| Application | Client |
|---|---|
| Spring Boot 3 with Jakarta EE | java-webflux-boot3 |
| Spring Boot 2 or Spring 5, reactive | java-webflux-boot2 |
| Spring MVC on the servlet stack, Spring 6 | java-resttemplate |
| Spring MVC on the servlet stack, Spring Boot 4 or Spring 7 | java-resttemplate-boot4 |
| Client | Requirements | Interface |
|---|---|---|
java-resttemplate |
Java 17, Spring Framework 6 | Synchronous: VerifyResponse verifyClientToken(VerifyRequest). Repeats requests for the 5xx and 429 status codes with an increasing interval. |
java-resttemplate-boot4 |
Java 17, Spring Boot 4 with Spring Framework 7 | Synchronous: VerifyResponse verifyClientToken(VerifyRequest). Repeats requests for the 5xx and 429 status codes with an increasing interval. |
java-webflux-boot2 |
Java 8, Spring Boot 2 with Spring WebFlux | Reactive: Mono<VerifyResponse> verifyClientToken(VerifyRequest) |
java-webflux-boot3 |
Java 17, Spring Boot 3 with Jakarta EE 10 | Reactive: Mono<VerifyResponse> verifyClientToken(VerifyRequest). Uses annotations from jakarta.* throughout. |
Embed the client
Embed the selected client with Maven. The example shows java-resttemplate:
<dependency>
<groupId>com.myrasec</groupId>
<artifactId>eu-captcha-java-resttemplate</artifactId>
<version>1.0.0</version>
<scope>compile</scope>
</dependency>
With Gradle, the entry is as follows:
You make the client from the sources with this command:
Verify the token
This example shows the synchronous verification with java-resttemplate:
import com.myrasec.client.ApiClient;
import com.myrasec.client.api.EuCaptchaApi;
import com.myrasec.client.model.VerifyRequest;
import com.myrasec.client.model.VerifyResponse;
import org.springframework.web.client.RestClientException;
ApiClient client = new ApiClient();
// client.setMaxAttemptsForRetry(3); // optional: retry up to 3 times on 5xx / 429
EuCaptchaApi api = new EuCaptchaApi(client);
VerifyRequest request = new VerifyRequest()
.sitekey("YOUR_SITEKEY")
.secret("YOUR_SECRET")
.clientIp(clientIp) // real end-user IP — see below
.clientToken(euCaptchaToken) // value of the "eu-captcha-response" POST field
.clientUserAgent(userAgent); // value of the User-Agent request header
try {
VerifyResponse response = api.verifyClientToken(request);
if (response.isTrainingMode()) {
// Training mode: real validation was not performed — always allow.
// Occurs only when the protection of the sitekey is switched off.
// An unknown sitekey or a wrong secret gives success=false.
allowAccess();
} else if (Boolean.TRUE.equals(response.getSuccess())) {
allowAccess();
} else {
denyAccess();
}
} catch (RestClientException e) {
// Network error or unexpected HTTP status.
// Decide your fallback policy: allow or deny.
log.error("EU CAPTCHA verification failed: {}", e.getMessage());
}
Reactive verification
The two clients for WebFlux give a Mono<VerifyResponse>. Put it in your processing chain instead of a .block() call:
import com.myrasec.client.ApiClient;
import com.myrasec.client.api.EuCaptchaApi;
import com.myrasec.client.model.VerifyRequest;
import com.myrasec.client.model.VerifyResponse;
import org.springframework.web.reactive.function.client.WebClientResponseException;
import reactor.core.publisher.Mono;
ApiClient client = new ApiClient(); // thread-safe; share a single instance
EuCaptchaApi api = new EuCaptchaApi(client);
VerifyRequest request = new VerifyRequest()
.sitekey("YOUR_SITEKEY")
.secret("YOUR_SECRET")
.clientIp(clientIp)
.clientToken(euCaptchaToken)
.clientUserAgent(userAgent);
Mono<VerifyResponse> result = api.verifyClientToken(request)
.map(response -> {
if (response.isTrainingMode()) {
allowAccess();
} else if (Boolean.TRUE.equals(response.getSuccess())) {
allowAccess();
} else {
denyAccess();
}
return response;
})
.onErrorResume(WebClientResponseException.class, e -> {
log.error("EU CAPTCHA verification failed: {}", e.getMessage());
return Mono.empty();
});
Get the IP address of the visitor
Always give the true IP address of the visitor, not the address of your upstream system.
In an application on the servlet stack:
// Without a proxy
String clientIp = httpServletRequest.getRemoteAddr();
// Behind a reverse proxy or CDN (use the header your provider documents)
String forwarded = httpServletRequest.getHeader("X-Forwarded-For");
if (forwarded != null && !forwarded.isBlank()) {
// X-Forwarded-For is a comma-separated list; the leftmost entry is the client IP
clientIp = forwarded.split(",")[0].trim();
}
In an application with WebFlux:
// In a Spring WebFlux handler (ServerWebExchange)
String clientIp = exchange.getRequest().getRemoteAddress().getAddress().getHostAddress();
// Behind a reverse proxy or CDN (use the header your provider documents)
String forwarded = exchange.getRequest().getHeaders().getFirst("X-Forwarded-For");
if (forwarded != null && !forwarded.isBlank()) {
clientIp = forwarded.split(",")[0].trim();
}
The X-Forwarded-For header contains a list with commas between the entries. The first entry is the IP address of the visitor.
Fields of the request
| Field | Type | Necessary | Content |
|---|---|---|---|
sitekey |
String | yes | Public sitekey from the Details view. |
secret |
String | yes | Secret key for the sitekey. |
clientIp |
String | yes | IPv4 or IPv6 address of the visitor. |
clientToken |
String | yes | Token from verify.js. The value can be empty. |
clientUserAgent |
String | yes | Value of the User-Agent header from the request of the visitor. |
Fields of the response
| Field | Type | Content |
|---|---|---|
success |
Boolean | true when the challenge passed. |
train |
Boolean | true when the training mode was on. The isTrainingMode() method reads this field. |
The endpoint is POST /verify at https://api.eu-captcha.eu/v1. An authorization is not necessary. The request identifies itself with the secret field.
Security
Attention
Do not set ApiClient.setDebugging(true) in production. In debug mode, the client writes the full JSON body to the log. VerifyRequest.toString() hides the secret. The transmitted body does not hide it.
Put the secret in an environment variable or in a key management system, never in the source code.
ApiClient is thread-safe. Make one instance and use it in the full application instead of a new instance for each request.
Full example
See Angular and Java.