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.

IoT Full-Stack Technology
IoT Full-Stack Technology
IoT Full-Stack Technology
A Lightweight Spring Boot Starter for Anti‑Repeat Submissions and Rate Limiting – Now on Maven Central

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/health

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

API 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 20

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

Summary

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.

RedisSpring BootAnnotationYAMLguardianrate-limitapi-protectionrepeat-submit
IoT Full-Stack Technology
Written by

IoT Full-Stack Technology

Dedicated to sharing IoT cloud services, embedded systems, and mobile client technology, with no spam ads.

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.