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.

Top Architect
Top Architect
Top Architect
How to Prevent Duplicate Submissions and Rate‑Limit APIs with the Guardian Spring Boot Starter

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 burst

YAML 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 json

Algorithms

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.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

BackendRedisrate limitingduplicate submissionAPI protection
Top Architect
Written by

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.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.