Overview
In this quickstart, you’ll add Fingerprint to a Java server using the Spring Boot framework to prevent fraudulent account creation.
The example use case we’ll use for this quickstart is stopping new account fraud, where attackers create multiple fake accounts to abuse promotions, exploit systems, or evade bans. However, the steps you’ll follow apply to most use cases. You can flag and block suspicious users by identifying the device behind each sign-up attempt, login, or transaction.
In this quickstart, you’ll learn how to:
- Set up a Spring Boot server with a Fingerprint integration
- Retrieve visitor identification data using the Server API
- Block bots and suspicious devices
- Prevent multiple signups from the same device
This guide focuses on the back-end integration and must be completed after identifying a visitor and generating a request ID. Before starting this quickstart, start with one of our front-end or mobile quickstarts to see how to identify a visitor in your front-end.
Estimated time: < 10 minutes
Prerequisites
Before you begin, make sure you have the following:
- A completed front-end or mobile Fingerprint implementation (See our quickstarts)
- Java (v11, v17, or v21) installed
- Your favorite code editor or IDE
- Basic knowledge of Java and Spring Boot
1. Get your secret API key
Before starting this quickstart, you should already have a front-end
Fingerprint implementation that sends the requestId to your server. If not,
pause here and check out one of our
front-end or
mobile quickstarts first.
If you’re ready:
- Sign in and go to the API keys page in the Fingerprint dashboard.
- Create a new secret API key.
- Copy it somewhere safe so you can use it to retrieve full visitor identification data from the Server API.
2. Set up your project
To get started, you need a basic Spring Boot server. If you already have a project, skip to the next step.
- Go to start.spring.io
- Configure the project:
- Project: Maven (or Gradle if you prefer)
- Language: Java
- Spring Boot: 3.x latest version
- Dependencies:
- Spring Web
- Spring Data JPA
- Everything else can remain as default
- Click Generate to download the starter project and unzip it.
- Open the project in your IDE and add these dependencies to your build file:
Maven (pom.xml):
<repositories>
<repository>
<id>jitpack.io</id>
<url>https://jitpack.io</url>
</repository>
</repositories>
<dependencies>
<!-- SQLite JDBC -->
<dependency>
<groupId>org.xerial</groupId>
<artifactId>sqlite-jdbc</artifactId>
<version>3.44.1.0</version>
</dependency>
<!-- Hibernate dialects for SQLite -->
<dependency>
<groupId>org.hibernate.orm</groupId>
<artifactId>hibernate-community-dialects</artifactId>
</dependency>
<!-- Fingerprint Server SDK -->
<dependency>
<groupId>com.github.fingerprintjs</groupId>
<artifactId>fingerprint-pro-server-api-java-sdk</artifactId>
<version>v7.8.0</version>
</dependency>
</dependencies>
Gradle (build.gradle):
repositories {
mavenCentral()
maven { url 'https://jitpack.io' }
}
dependencies {
implementation 'org.xerial:sqlite-jdbc:3.44.1.0'
implementation 'org.hibernate.orm:hibernate-community-dialects'
implementation 'com.github.fingerprintjs:fingerprint-pro-server-api-java-sdk:v7.8.0'
}
Note: This quickstart is written for Fingerprint SDK version 7.x. Additional installation methods detailed in our SDK documentation .
You can run the project with:
- Update the file at
src/main/resources/application.properties with the following content:
spring.application.name=demo
server.port=3000
spring.datasource.url=jdbc:sqlite:database.db
spring.datasource.driver-class-name=org.sqlite.JDBC
spring.jpa.database-platform=org.hibernate.community.dialect.SQLiteDialect
spring.jpa.hibernate.ddl-auto=create-drop
Note: spring.jpa.hibernate.ddl-auto=create-drop is for demo purposes to reset the database each time the app starts.
- Create a basic controller
src/main/java/com/example/demo/AccountController.java:
package com.example.demo;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.Map;
@RestController
@RequestMapping("/api")
public class AccountController {
@PostMapping("/create-account")
public ResponseEntity<Map<String, String>> createAccount(@RequestBody CreateAccountRequest request) {
return ResponseEntity.ok(Map.of("status", "Account created!"));
}
}
- Create a data transfer object (DTO) class to represent the JSON payload your front end will send when creating an account at
src/main/java/com/example/demo/CreateAccountRequest.java:
CreateAccountRequest.java
package com.example.demo;
public class CreateAccountRequest {
private String requestId;
private String username;
private String password;
public String getRequestId() { return requestId; }
public void setRequestId(String requestId) { this.requestId = requestId; }
public String getUsername() { return username; }
public void setUsername(String username) { this.username = username; }
public String getPassword() { return password; }
public void setPassword(String password) { this.password = password; }
}
Here we are running the server at port 3000 and have created a new POST route for account creation. Note that the /api/create-account route should match what you have set up in your front-end implementation where you are sending the Fingerprint requestId to your server. Your server will receive the initial identification information from identifying a visitor on the front end and use it to get the full visitor data on the back end.
3. Initialize Fingerprint and retrieve visitor data
Now you’ll configure the Fingerprint server SDK using your secret API key and use it to fetch detailed visitor data for each signup attempt.
When making the initial visitor identification request in the front end, you received a requestId. This ID is unique to each identification event. Your server can then use the Fingerprint Events API to retrieve complete identification data, including the trusted visitor ID and other actionable insights like whether they are using a VPN or are a bot.
- Create a Fingerprint configuration class
src/main/java/com/example/demo/FingerprintConfig.java:
package com.example.demo;
import com.fingerprint.api.FingerprintApi;
import com.fingerprint.sdk.ApiClient;
import com.fingerprint.sdk.Configuration;
import com.fingerprint.sdk.Region;
import org.springframework.context.annotation.Bean;
@org.springframework.context.annotation.Configuration
public class FingerprintConfig {
@Bean
public FingerprintApi fingerprintApi() {
ApiClient client = Configuration.getDefaultApiClient(
"<your-secret-api-key>",
Region.GLOBAL
);
return new FingerprintApi(client);
}
}
For a production implementation make sure to store and reference your secret key securely.
- Update your
AccountController to use the Fingerprint API and retrieve the full visitor identification details with getEvent():
package com.example.demo;
import com.fingerprint.api.FingerprintApi;
import com.fingerprint.model.EventsGetResponse;
import com.fingerprint.sdk.ApiException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.Map;
@RestController
@RequestMapping("/api")
public class AccountController {
@Autowired
private FingerprintApi fingerprintApi;
@PostMapping("/create-account")
public ResponseEntity<Map<String, String>> createAccount(@RequestBody CreateAccountRequest request) {
try {
EventsGetResponse event = fingerprintApi.getEvent(request.getRequestId());
return ResponseEntity.ok(Map.of("status", "Account created!"));
} catch (ApiException e) {
return ResponseEntity.badRequest()
.body(Map.of("error", "Error processing request"));
}
}
}
Using the requestId the Fingerprint server client will retrieve the full data for the visitor identification request. The returned object will contain the visitor ID, IP address, device and browser details, and Smart Signals like bot detection, incognito mode detection, and detections for VPN or virtual machine use.
You can see a full example of the event structure, and test it with your own device, in our demo playground.
For additional checks to ensure the validity of the data coming from your front end view how to protect from client-side tampering and replay attacks in our documentation.
4. Block bots and suspicious devices
This optional step uses the Bot Detection Smart Signal which is available only
on paid plans.
A simple but powerful way to prevent fraudulent account creation is to block automated signups that come from bots. The event object includes the Bot Detection Smart Signal that flags automated activity, making it easy to reject bot traffic.
- Continuing in your
/api/create-account route, check the bot signal returned in the event object:
@PostMapping("/create-account")
public ResponseEntity<Map<String, String>> createAccount(@RequestBody CreateAccountRequest request) {
try {
EventsGetResponse event = fingerprintApi.getEvent(request.getRequestId());
if (event.getProducts().getBotd().getData().getBot().getResult() !=
com.fingerprint.model.BotdBotResult.NOT_DETECTED) {
return ResponseEntity.status(403)
.body(Map.of("error", "Failed to create account."));
}
return ResponseEntity.ok(Map.of("status", "Account created!"));
} catch (ApiException e) {
return ResponseEntity.badRequest()
.body(Map.of("error", "Error processing request"));
}
}
This signal returns good for known bots like search engines, bad for automation tools, headless browsers, or other signs of automation, and notDetected when no bot activity is found. You can also layer in other Smart Signals to catch more suspicious devices. For example, you can use Fingerprint’s Suspect Score to determine when to add additional friction to create an account.
5. Prevent multiple signups from the same device
To catch repeated signups from the same device, you can use the visitorId from the Fingerprint identification event. By saving this ID alongside each created account, you can easily detect and block duplicate signups from the same device. We’ll be using a simple database to demonstrate how this works with SQLite.
- Create an Account entity
src/main/java/com/example/demo/Account.java:
package com.example.demo;
import jakarta.persistence.*;
@Entity
@Table(name = "accounts")
public class Account {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String username;
private String password;
private String visitorId;
public Account() {}
public Account(String username, String password, String visitorId) {
this.username = username;
this.password = password;
this.visitorId = visitorId;
}
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
public String getUsername() { return username; }
public void setUsername(String username) { this.username = username; }
public String getPassword() { return password; }
public void setPassword(String password) { this.password = password; }
public String getVisitorId() { return visitorId; }
public void setVisitorId(String visitorId) { this.visitorId = visitorId; }
}
- Create a repository interface
src/main/java/com/example/demo/AccountRepository.java:
package com.example.demo;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface AccountRepository extends JpaRepository<Account, Long> {
long countByVisitorId(String visitorId);
}
- Update your
AccountController to use the database and check for existing accounts:
package com.example.demo;
import com.fingerprint.api.FingerprintApi;
import com.fingerprint.model.EventsGetResponse;
import com.fingerprint.sdk.ApiException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.Map;
@RestController
@RequestMapping("/api")
public class AccountController {
@Autowired
private FingerprintApi fingerprintApi;
@Autowired
private AccountRepository accountRepository;
@PostMapping("/create-account")
public ResponseEntity<Map<String, String>> createAccount(@RequestBody CreateAccountRequest request) {
try {
EventsGetResponse event = fingerprintApi.getEvent(request.getRequestId());
if (event.getProducts().getBotd().getData().getBot().getResult() !=
com.fingerprint.model.BotdBotResult.NOT_DETECTED) {
return ResponseEntity.status(403)
.body(Map.of("error", "Failed to create account."));
}
String visitorId = event.getProducts().getIdentification().getData().getVisitorId();
long existingAccounts = accountRepository.countByVisitorId(visitorId);
if (existingAccounts > 0) {
return ResponseEntity.status(429)
.body(Map.of("error", "Failed to create account."));
}
Account newAccount = new Account(request.getUsername(), request.getPassword(), visitorId);
accountRepository.save(newAccount);
return ResponseEntity.ok(Map.of("status", "Account created!"));
} catch (ApiException e) {
return ResponseEntity.badRequest()
.body(Map.of("error", "Error processing request"));
}
}
}
This gives you a basic system to detect and block repeat signups. You can expand on this by allowing a limited number of accounts per device, adjusting your response based on business rules, only evaluating recent signups, etc.
This is a minimal example to show how to use the Fingerprint SDK. In a real
application, make sure to implement proper security practices, especially
around password handling and storage.
6. Test your implementation
Now that everything is set up, you can test the full flow using your existing front end.
Before you test
If your front end is running on a different port (like localhost:5173 or localhost:3001), you may run into CORS issues for testing. To quickly fix this for local development & testing:
- Add the
@CrossOrigin annotation to your AccountController:
@RestController
@RequestMapping("/api")
@CrossOrigin(origins = "*", allowedHeaders = "*") // Add this
public class AccountController {
Test the implementation
- Start your Spring Boot server:
- In your front end, trigger a sign-up request that sends the
requestId, username, and password to your /api/create-account endpoint. To see the reply messages make sure to parse and display or console log the response from your server.
- Within your front end, input a username and password to create a user. Then try to create another user and see that the second attempt will be rejected.
- Bonus: Try creating an account using a headless browser.
Next steps
You now have a working back-end fraud check using Fingerprint. From here, you can expand your logic with more Smart Signals, adjust thresholds based on your risk tolerance, or introduce additional checks for suspicious users.
These same techniques apply to a wide range of fraud prevention use cases, from detecting fake reviews to blocking payment abuse or preventing account takeovers.
To go further, check out our use case tutorials for step-by-step guides tailored to specific problems you can solve with Fingerprint.
Check out these related resources: