---
title: "Spring Boot security middleware: quickstart · camada"
description: "Keep credential stuffers, scrapers and exploit scanners out of your Spring Boot app: install from GitHub, two lines and one env var."
url: https://www.camada.app/docs/quickstart/spring
last_updated: 2026-10-30
---

Quickstart

# Spring Boot quickstart

Installed from GitHub ([camada-app/camada-java](https://github.com/camada-app/camada-java)).

## 1. Install

Not on Maven Central yet. From your project's folder: clone it next to the project, build it into your local Maven repository, then add the dependency.

```sh
git clone https://github.com/camada-app/camada-java ../camada-java
mvn -q -f ../camada-java install -DskipTests

<!-- then, in pom.xml -->
<dependency>
  <groupId>dev.camada</groupId>
  <artifactId>camada</artifactId>
  <version>0.2.0</version>
</dependency>
```

## 2. Environment

The key is printed once when you create a project in the app. Set these as environment variables: export them in the shell that starts your app, or add them in your host's settings. camada reads only the environment, and Spring Boot does not pass application.properties or a .env file to it.

shell

```sh
export CAMADA_KEY=<ingest_token>.<snap_token>
export CAMADA_INGEST_URL=http://localhost:8787   # dev only; defaults to production ingest
```

## 3. Wire it in

```java
// CamadaConfig.java, beside your @SpringBootApplication class
package com.example.demo;   // your application's package

import dev.camada.servlet.CamadaFilter;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;

@Configuration
class CamadaConfig {
  @Bean
  FilterRegistrationBean<CamadaFilter> camadaFilter() {
    var reg = new FilterRegistrationBean<>(new CamadaFilter());
    reg.setOrder(Ordered.HIGHEST_PRECEDENCE);   // first, so camada answers before routing
    return reg;
  }
}
```

- A plain jakarta.servlet.Filter: Tomcat, Jetty and Undertow take it as is, from web.xml or addFilter at the front of the chain.
- Keep server.forward-headers-strategy=none (the default) so camada judges X-Forwarded-For itself.

## 4. Add the beacon to HTML pages

Optional but recommended. The beacon cuts false positives by showing which sessions are real browsers. Use the helper.

```java
// PageController.java, beside CamadaConfig.java: the tag goes in the <head> of the pages you render
package com.example.demo;   // your application's package

import dev.camada.Camada;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
class PageController {
  @GetMapping(value = "/", produces = "text/html")
  String home(HttpServletRequest request) {
    return "<html><head>" + Camada.scriptTag(request) + "</head><body>…</body></html>";
  }
}
```

## 5. Send app outcomes

Login failures, signups and checkout declines are the signals no edge position sees. The identifier is HMAC-hashed in your process. How they catch [credential stuffing](https://www.camada.app/use-cases/credential-stuffing).

```java
// in a PageController handler, once your own sign-in check fails (email: the account it was for)
Camada.track(request, "login_failed", email);
```

Runtime

## What it does per request

1. Refreshes the snapshot off-path on a daemon thread at the cadence your tenant config sets (ETag/304, gzip on the wire). A cold start fails open.
2. Resolves the client IP from getRemoteAddr() per your trusted-proxy setting. Raw X-Forwarded-For is never trusted without it; keep server.forward-headers-strategy=none.
3. Your ordered custom rules first, then allow, block, challenge. A block gets a 403 with x-block-reason before your app runs; a challenge serves the proof-of-work page on a page load, and an API call reaches your app with an x-camada-challenge: 1 response header. The event still ships.
4. Serves /_cam/b.js (the beacon, first-party) and relays /_cam/fp posts onto the event batch with the resolved client IP.
5. Sets x-rid and the _sfp session cookie before the chain runs, and when the response is done (or in AsyncListener.onComplete) ships one batched, redacted event carrying the Spring MVC route pattern.

Limits

## Caveats at this position

- Enforcement at this position covers IP, path, user-agent and request-header conditions. ASN, country and TLS-fingerprint entries fail open in-app.
- The event carries the header order as the container hands it over.
- The request that builds the engine is answered cold. Camada.getDefault().warmUp(5000) at startup waits for the boot poll, bounded, and never throws.
- Both threads are daemons; pending events drain from a JVM shutdown hook, on SIGTERM as well as a normal exit.
- CamadaFilter handles the original REQUEST dispatch only; ASYNC, ERROR, FORWARD and INCLUDE pass through. CAMADA_SERVERLESS=1 hands refreshes to the daemon thread from a per-request staleness check.

Kill switch: `CAMADA_DISABLED=1`, checked per request. All other variables are listed in the [docs index](https://www.camada.app/docs); the package's options and deployment notes are on the [Java page](https://www.camada.app/docs/java).

## Sitemap

[Every page on this site](https://www.camada.app/sitemap.md)
