Java¶
For Java, there are three clients. All three are made from the same OpenAPI description and 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 |
| 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-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 when the sitekey does not exist, the secret is wrong,
// or the sitekey is configured with train=true.
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¶
Warning
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.