A Lightweight Spring Boot Starter for Anti‑Repeat Submissions and Rate Limiting – Now on Maven Central
Guardian is a lightweight Spring Boot starter that provides both anti‑repeat‑submission protection and API rate limiting; it supports annotation and YAML configuration, flexible key scopes (user, IP, global), Redis or local storage, customizable response modes, white‑list handling, and built‑in observability, making it a drop‑in solution for single‑service projects.
Anti‑repeat submission
Three‑step usage
Add the Maven dependency:
<dependency>
<groupId>io.github.biggg-guardian</groupId>
<artifactId>guardian-repeat-submit-spring-boot-starter</artifactId>
<version>1.3.0</version>
</dependency>Annotate the controller method:
@PostMapping("/submit")
@RepeatSubmit(interval = 10, message = "订单正在处理,请勿重复提交")
public Result submitOrder(@RequestBody OrderDTO order) {
return orderService.submit(order);
}Run the application – the interceptor becomes active automatically.
Why not use raw Redis SETNX?
Key composition – a simple userId+url cannot distinguish different request bodies (e.g., buying product A vs B). Guardian builds the key from user/IP scope, request URI and a Base64‑encoded JSON representation of the body.
Request body handling – POST bodies are streams that can be read only once. RepeatableRequestFilter caches the body so it can be used for key generation.
User identification – when userId is null, Guardian falls back to sessionId and then to client IP, guaranteeing a non‑null key.
Exception safety – if business logic throws an exception while the lock is held, afterCompletion releases the lock automatically, preventing stale blocks.
Whitelist – endpoints such as health checks can be excluded via exclude-urls, which has the highest priority.
YAML bulk configuration
When many endpoints share the same rule, configure them in application.yml:
guardian:
repeat-submit:
storage: redis
key-encrypt: md5
urls:
- pattern: /api/order/**
interval: 10
key-scope: user
message: "订单正在处理,请勿重复提交"
- pattern: /api/sms/send
interval: 60
key-scope: ip
exclude-urls:
- /api/public/**
- /api/healthYAML rules have higher priority than annotations, and exclude-urls always wins.
Response handling
Exception mode (default) – throws RepeatSubmitException; a global exception handler can translate it to a JSON response.
JSON mode – the interceptor writes a JSON payload directly, e.g. {"code":500,"msg":"...","timestamp":...}. Custom formats are supported by registering a RepeatSubmitResponseHandler bean.
Running without Redis
Set storage: local to use an in‑memory ConcurrentHashMap with a daemon thread that cleans expired keys every five minutes. Production environments should use Redis for distributed safety.
Context‑path handling
When server.servlet.context-path is set, Guardian matches both the full URI (including the context path) and the URI stripped of the context path, so rules work regardless of how the path is written.
Internal processing flow
Request → RepeatableRequestFilter (caches body) → RepeatSubmitInterceptor
├─ whitelist → pass
├─ YAML rule match
├─ @RepeatSubmit annotation match
│ → not matched → pass
▼
KeyGenerator (user/ip/global) → KeyEncrypt (optional MD5) → Storage.tryAcquire()
├─ success → write lock with TTL, allow request
└─ failure → response‑mode (exception or json)
▼
Business execution
├─ normal → lock expires naturally
└─ exception → afterCompletion releases lockAPI rate limiting
Usage
Add the starter dependency:
<dependency>
<groupId>io.github.biggg-guardian</groupId>
<artifactId>guardian-rate-limit-spring-boot-starter</artifactId>
<version>1.3.0</version>
</dependency>Annotation examples:
@RateLimit(qps = 10) // sliding window: at most 10 requests per second
@RateLimit(qps = 5, capacity = 20, algorithm = RateLimitAlgorithm.TOKEN_BUCKET) // token bucket: 5 tokens added per second, burst up to 20YAML bulk configuration:
guardian:
rate-limit:
urls:
- pattern: /api/sms/send
qps: 1
rate-limit-scope: ip
message: "短信发送过于频繁"
- pattern: /api/seckill/**
qps: 10
capacity: 50
algorithm: token_bucket
rate-limit-scope: global
exclude-urls:
- /api/public/**Algorithm comparison
Sliding window counts requests within a fixed time window; the count never exceeds the threshold. Suitable for SMS sending or login attempts.
Token bucket allows bursts: tokens accumulate when idle and are consumed instantly. Ideal for flash‑sale or burst traffic scenarios.
Rate‑limit dimensions
GLOBAL – a single counter for the whole API (e.g., site‑wide search).
IP – independent counters per client IP (e.g., SMS, verification codes).
USER – independent counters per authenticated user (e.g., user‑specific actions).
Response handling
Exception mode (default) – throws RateLimitException for global handlers.
JSON mode – writes a JSON error directly.
Custom RateLimitResponseHandler beans can override the default behavior.
Concurrency safety
Redis implementations use Lua scripts; Redis executes Lua single‑threaded, guaranteeing atomicity.
Local cache synchronizes on the key level ( synchronized), so different keys do not block each other.
Both modules use atomic Redis commands ( SET NX EX) or ConcurrentHashMap atomic methods for lock acquisition.
Pluggable architecture
Core components are defined as interfaces and instantiated with @ConditionalOnMissingBean. Users can replace any default implementation (key generator, encryptor, storage, response handler) by providing their own bean.
Observability
Enable logging via log-enabled: true to record every block/pass event.
Actuator endpoints expose statistics:
GET /actuator/guardian-repeat-submit // repeat‑submit metrics
GET /actuator/guardian-rate-limit // rate‑limit metrics (total requests, passes, blocks, block rate, top blocked APIs, etc.)Project structure
guardian-parent
├── guardian-core // shared utilities (UserContext, response handlers)
├── guardian-repeat-submit
│ ├── guardian-repeat-submit-core
│ └── guardian-repeat-submit-spring-boot-starter
├── guardian-rate-limit
│ ├── guardian-rate-limit-core
│ └── guardian-rate-limit-spring-boot-starter
├── guardian-storage-redis // Redis storage shared by both modules
└── guardian-example // demo projectSummary
Guardian provides two independent Spring Boot starters:
Anti‑repeat submission – blocks rapid duplicate requests. Supports annotation and YAML configuration, user/IP/global key scopes, automatic lock release on exceptions, context‑path compatibility, and selectable response modes.
API rate limiting – caps request frequency. Supports sliding‑window and token‑bucket algorithms, burst handling, three scopes (global, IP, user), annotation and YAML configuration, and customizable response handling.
Both starters are lightweight, require only Redis (or optional local cache) and expose full observability via logging and Actuator endpoints. The architecture is fully pluggable, allowing custom implementations of key generation, encryption, storage and response handling.
IoT Full-Stack Technology
Dedicated to sharing IoT cloud services, embedded systems, and mobile client technology, with no spam ads.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
