Spring MVC and Spring Boot Setup
Source and package versions
One AIWAF repository.
Python: py/aiwaf/ · Node.js: js/ · Rust/WASM: rust/ · Java: java/.
Source and canonical README.
Versions shown describe the source checkout; package releases are independent.
Current Python/Node source targets are aiwaf 1.1.8 (Python) and
aiwaf 1.1.5 (npm), with Rust/Python 0.2.7 and
WASM 0.2.5 and Java 1.3.5. Sandbox guides retain their separately pinned builds.
How AIWAF works: system architecture
Integrate native Java enforcement into Spring and verify how the adapter observes requests and responses.
You need Java 17+, Maven, and an existing Spring Boot or Spring MVC servlet application compatible with Spring Framework 7 and Jakarta Servlet 6.1. These are integration snippets, not a standalone generated application. First confirm that an ordinary endpoint runs without AIWAF.
1. Add the Java dependency
pom.xml / dependencies
Java source version 1.3.5 is declared in java/pom.xml. It targets Java 17, Spring Framework 7.0, and Jakarta Servlet 6.1, and runs natively without Rust. Use the Maven dependency below once this version is published to Maven Central; source builds are available from the monorepo.
<dependency>
<groupId>io.github.aiwaf-project</groupId>
<artifactId>aiwaf-java</artifactId>
<version>1.3.5</version>
</dependency>Build from source when needed
If the required version is unavailable in your registry, install the reviewed monorepo source in your local Maven repository:
git clone https://github.com/aiwaf-project/aiwaf.git
cd aiwaf/java
mvn -Dgpg.skip=true installReturn to your application checkout afterward. Match its dependency version to the version built from java/pom.xml.
2. Register protection once
Spring Boot applications are protected automatically. Adding aiwaf-java creates AiwafConfig, AiwafEngine, and the ordered AiwafFilter; discovers live MVC routes, route annotations, and JPA UUID fields; and applies request controls before the controller plus post-response scoring with the real status and timing. Set aiwaf.enabled=false to disable auto-configuration.
src/main/resources/application.properties
The following is a production-oriented Redis example. It requires a reachable standalone Redis service; use the default memory backend for a first single-process integration, and add geo/distributed policy deliberately.
aiwaf.rate-limit.max=60
aiwaf.rate-limit.window-seconds=60
aiwaf.geo.enabled=true
aiwaf.geo.blocked-countries=CN,RU
aiwaf.path-manifest.path=.aiwaf/paths.json
# Use one namespace and shared enforcement state across application instances.
aiwaf.storage.backend=redis
aiwaf.storage.redis-url=redis://localhost:6379/0
aiwaf.storage.key-prefix=my-service
# Redis is fail-closed by default. Every wait is bounded.
aiwaf.storage.redis-failure-mode=fail_closed
aiwaf.storage.redis-connection-timeout-millis=1000
aiwaf.storage.redis-socket-timeout-millis=2000
aiwaf.storage.redis-pool-max-total=32
aiwaf.storage.redis-pool-max-idle=16
aiwaf.storage.redis-pool-min-idle=0
aiwaf.storage.redis-pool-max-wait-millis=250
Set aiwaf.config-file=/etc/aiwaf/config.json (or AIWAF_CONFIG_FILE) to load the same nested JSON configuration used by the Python runtime. Environment variables and Spring properties override file values. AiwafConfigFileCore also exposes load, save, deep-merge, and validation APIs for non-Spring applications.
Define an AiwafConfig, AiwafEngine, or AiwafFilter bean only when the application needs a custom replacement. For plain Spring MVC without Boot, register the filter directly; passing the application context enables the same live route discovery:
@Bean
AiwafFilter aiwafFilter(AiwafEngine engine, ApplicationContext context) {
return new AiwafFilter(engine, context);
}
The live mapping integration honors @AiwafExempt, @AiwafExemptFrom, @AiwafOnly, and @AiwafRequireProtection without a controller list. Do not register AiwafInterceptor and AiwafFilter as independent enforcement layers for the same request.
Committed, streaming, and asynchronous responses are recorded but are not replaced after bytes have been sent.
UUID protection uses Python-compatible weighted signals: malformed input, valid UUID requests that resolve to 404, and successful requests that decay the score. UUID-valued query parameters and {uuid} route variables are supported. SpringUuidModelLookup optionally discovers UUID primary keys and unique UUID properties through the JPA metamodel; omit it when the application does not use JPA.
Generate a route manifest from the live Spring application rather than supplying controller methods manually:
java -cp "your-app.jar:aiwaf-java-1.3.5.jar" \
com.aiwaf.cli.AiwafConsole init \
--app com.example.Application \
--output .aiwaf/paths.json
The command starts the application, reads every registered RequestMappingHandlerMapping, writes the shared schema_version/framework/context_hash/routes document, and then closes the temporary application context. At runtime, AiwafEngine loads this file automatically and appends its disabled middleware and rate-limit overrides after explicit pathRules, so explicit equal-prefix rules retain precedence. Set pathManifestEnabled=false or aiwaf.path-manifest.enabled=false to opt out.
Configure and operate
| Area | Main AiwafConfig fields |
|---|---|
| Rate limiting | rateLimitEnabled, rateLimitScope, rateLimitWindowSeconds, rateLimitMax, rateLimitFloodThreshold |
| Headers | headerValidationEnabled, requiredHeadersByMethod, minHeaderQualityScore, size/count limits |
| Request | body, parameter, method, and content-type limits |
| Proxies | trustedProxyCidrs, maxForwardedForEntries |
| Exemptions | exemptIps, exemptPaths, autoExemptPathPrefixes |
| GeoIP | geoBlockEnabled, geoAllowedCountries, geoBlockedCountries, geoIpDatabasePath, cache limits |
| Route manifest | pathManifestEnabled, pathManifestPath, pathRules |
| UUID checks | uuidTamperEnabled, uuidScoreWindowSeconds, uuidScoreBlockThreshold, signal weights, uuidParameterNames |
| AI | aiEnabled, aiModelPath, aiAnomalyScoreThreshold |
| Storage | storageBackend, storageFilePath, storageRedisUrl, storageKeyPrefix, storageRedisFailureMode, Redis timeout and pool limits |
| Telemetry | observabilityEnabled |
GeoIP uses the bundled MMDB through the native Java reader; it does not require the external mmdblookup executable. A valid X-Country value is accepted only from a peer in trustedProxyCidrs; otherwise AIWAF resolves the client address itself. Default blocklist calls use Python-compatible reputation scoring and temporary escalation (15 minutes, 1 hour, then 24 hours). Use BlocklistManager.blockPermanent(ip, reason) for an explicit permanent administrative block.
storageBackend="redis" makes rate-limit buckets, honeypot timing, weighted UUID scores, recent-request anomaly state, blocklist data, exemptions, geo policy, and learned keywords visible to every application instance using the same key prefix. Redis enforcement updates use atomic server-side scripts. Memory, file, CSV, and SQLite engines keep request state isolated per AiwafEngine; constructing another engine no longer replaces an existing engine's stores. The static RuntimeStorage and BlocklistManager methods remain compatibility facades for command-line and administrative code.
The Java 1.3 Redis backend supports standalone Redis only. Sentinel and Cluster configurations are rejected during validation instead of silently operating with incomplete semantics. fail_closed is the default: an unavailable Redis instance prevents startup or raises a request-path storage failure, which results in the application denying availability rather than bypassing protection. fail_open permits startup and returns non-blocking enforcement fallbacks while Redis is unavailable; failed mutations return false at the StorageBackend boundary. Connection, socket, pool size, and pool wait limits are bounded and configurable. RedisStorage.diagnostics() reports operation/failure counts, latency, and live pool utilization.
Redis keys include a state-schema version, and each base key prefix has an atomic compatibility marker. Releases using the same schema can share state during a rolling upgrade. A runtime with an incompatible schema refuses the namespace—even in fail_open mode. Use a unique key prefix per application and environment; deploy an incompatible schema with a new prefix, migrate deliberately, then retire the old namespace. Serialized persistent values remain JSON within a schema version.
New Java models use the Python-aligned six-feature schema and are written as safe JSON containing the Python aiwaf_rust.IsolationForest state. Python can restore Java-generated forest state, and Java can load Python Rust-model artifacts. Existing signed Java binary artifacts, including nine-feature models, remain readable through the restricted legacy loader. Scikit-learn object artifacts are intentionally unsupported.
Java operational commands mirror the common Python workflow:
aiwaf-cli status
aiwaf-cli diagnose --model model.json
aiwaf-cli blocked
aiwaf-cli unblock 203.0.113.10
aiwaf-cli clear
aiwaf-cli train --events events.json --out model.json --backend java
aiwaf-cli replay --cases replay.json
aiwaf-cli model status --path model.json
aiwaf-cli logs --dir aiwaf_logs
Build Java from the repository root:
cd java
mvn test
mvn -Dgpg.skip=true package
The build produces the main, source, and Javadoc JARs under java/target/. The main JAR embeds the root license as META-INF/LICENSE. FastR training is optional and falls back to the Java trainer when R or jsonlite is unavailable.
Verify the deployed adapter
- Build the existing application with
mvn testand its normal packaging command. Resolve framework/Servlet compatibility errors before starting the server. - Start the application with its existing launcher and request a known non-exempt handler through a browser.
- Inspect the enabled policy, observed client IP, response status, and duration. Private-address exemptions can affect local tests.
- Exercise a deliberate block or rate boundary on a staging route, and confirm the handler response is not returned for the rejected request.
- For distributed deployments, repeat across instances and verify the selected namespace, backend, and failure policy.
A successful normal request proves reachability. It does not prove that every middleware is selected or that a trained model is loaded. Use the Java architecture guide to understand request-only versus response-aware decisions.
