How to Prevent Duplicate Submissions and Rate‑Limit APIs with the Guardian Spring Boot Starter
This article explains how the Guardian Spring Boot starter provides a lightweight solution for preventing duplicate API submissions and applying rate‑limiting, covering Maven dependencies, annotation and YAML configurations, key generation strategies, response handling modes, Redis versus local storage, context‑path compatibility, concurrency safety, and built‑in monitoring features.
Overview
Guardian is a lightweight Spring Boot starter that provides two independent modules: anti‑duplicate‑submission and API rate limiting. Both modules are published on Maven Central (v1.3.0) and can be used separately.
Dependency
<dependency>
<groupId>io.github.biggg-guardian</groupId>
<artifactId>guardian-repeat-submit-spring-boot-starter</artifactId>
<version>1.3.0</version>
</dependency>
<dependency>
<groupId>io.github.biggg-guardian</groupId>
<artifactId>guardian-rate-limit-spring-boot-starter</artifactId>
<version>1.3.0</version>
</dependency>GitHub repository: https://github.com/BigGG-Guardian/guardian
Anti‑Duplicate Submission
Quick start
Add the Maven dependency.
Annotate the controller method with @RepeatSubmit and configure interval and message if needed.
Run the application; the interceptor is auto‑registered.
@PostMapping("/submit")
@RepeatSubmit(interval = 10, message = "订单正在处理,请勿重复提交")
public Result submitOrder(@RequestBody OrderDTO order) {
return orderService.submit(order);
}Key generation
The key is composed of the user identifier (userId, sessionId or client IP), request URI and a Base64‑encoded JSON representation of the request parameters. If the user is not logged in the framework falls back to sessionId and then to IP, guaranteeing a non‑null key.
Response modes
exception (default) – throws RepeatSubmitException which can be handled by a global @RestControllerAdvice.
json – writes a JSON payload directly, e.g. {"code":500,"msg":"...","timestamp":...}.
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(RepeatSubmitException.class)
public Result handle(RepeatSubmitException e) {
return Result.fail(e.getMessage());
}
}YAML bulk configuration
guardian:
repeat-submit:
storage: redis # or "local" for in‑memory cache
key-encrypt: md5
urls:
- pattern: /api/order/**
interval: 10
key-scope: user
message: "订单正在处理,请勿重复提交"
- pattern: /api/sms/send
interval: 60
key-scope: ip
message: "短信发送过于频繁"
exclude-urls:
- /api/public/**YAML rules have higher priority than annotations; exclude-urls is the highest‑priority whitelist.
Context‑path compatibility
The matcher checks both the full request URI (including server.servlet.context-path) and the URI without the context path, so configuration works regardless of a prefixed path.
API Rate Limiting
Quick start
Add the rate‑limit starter dependency.
Annotate a method with @RateLimit or configure it in YAML.
@RateLimit(qps = 10) // sliding‑window, max 10 requests per second
@RateLimit(qps = 5, capacity = 20, algorithm = RateLimitAlgorithm.TOKEN_BUCKET) // token bucket with burstYAML 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/**
response-mode: exception # or jsonAlgorithms
Sliding Window – counts requests in a fixed time window; strict limit.
Token Bucket – tokens are added at a fixed rate; allows bursts up to the bucket capacity.
Scope
GLOBAL – a single counter for the whole interface.
IP – separate counters per client IP.
USER – separate counters per authenticated user.
Concurrency safety
When Redis is used, both algorithms run inside a Lua script, guaranteeing atomicity. For local in‑memory storage, synchronized blocks are applied at the key level and ConcurrentHashMap atomic methods are used.
Monitoring
GET /actuator/guardian-repeat-submit– statistics for duplicate‑submission. GET /actuator/guardian-rate-limit – statistics for rate limiting, including total requests, passes, blocks, block rate and top blocked APIs.
Logging can be enabled with log-enabled: true to output intercept/allow events.
Architecture
The project is modular:
guardian-core – shared utilities (e.g., UserContext, response handlers).
guardian-repeat-submit – key generator, storage abstraction and Spring Boot starter.
guardian-rate-limit – analogous components for rate limiting.
guardian-storage-redis – Redis implementation shared by both modules.
guardian-example – sample application.
All core components are defined as interfaces and are loaded with @ConditionalOnMissingBean, allowing developers to replace the default implementations by providing their own beans.
Key implementation notes
Key generation includes request parameters; a RepeatableRequestFilter caches the request body to enable multiple reads.
Three‑level user identification (userId → sessionId → IP) prevents null keys.
Locks are released automatically in afterCompletion when an exception occurs, avoiding stale locks.
Whitelist ( exclude-urls) has the highest priority and bypasses all checks.
Both modules support Redis or local in‑memory storage; local storage uses a daemon thread to clean expired entries every 5 minutes.
Usage without Redis
Set guardian.repeat-submit.storage: local or guardian.rate-limit.storage: local to use an in‑memory ConcurrentHashMap implementation. This is suitable for development or small single‑node applications.
Pluggable design
Key generators, encryptors, storage, and response handlers are all extensible. Register a bean of the corresponding type to override the default implementation.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Top Architect
Top Architect focuses on sharing practical architecture knowledge, covering enterprise, system, website, large‑scale distributed, and high‑availability architectures, plus architecture adjustments using internet technologies. We welcome idea‑driven, sharing‑oriented architects to exchange and learn together.
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.
