Implementing Interface Rate Limiting with Spring Interceptor, Redis, and Custom Annotations
This article demonstrates how to prevent abusive API calls in a Spring Boot application by using an Interceptor together with Redis for counting requests, and extends the solution with configurable custom annotations and reflection to allow per‑endpoint rate‑limit settings.
Preface: The article introduces a demo for preventing interface abuse using a Spring Interceptor combined with Redis.
Principle: It identifies a visitor by concatenating the IP address and request URI, then counts accesses in Redis to enforce limits such as a maximum number of calls within a given time window.
Project: The source code is hosted on GitHub; the key logic resides in the
@Slf4j public class AccessLimintInterceptor implements HandlerInterceptor { ... }class, which retrieves the IP and URI, checks lock and count keys in Redis, updates counters, and throws a CommonException(ResultCode.ACCESS_FREQUENT) when limits are exceeded.
Self‑question: The initial implementation works but lacks flexibility because all protected endpoints share the same second, maxTime and forbiddenTime values.
Interface freedom: By configuring interceptor mapping rules or using custom annotations, specific interfaces can have independent limits, avoiding the need for multiple interceptors.
Custom annotation + reflection: A new @AccessLimit(second, maxTime, forbiddenTime) annotation can be placed on methods or classes. The interceptor obtains the annotation via reflection, determines whether the class or method defines the limits, and applies them accordingly. The updated interceptor code looks like:
@Slf4j public class AccessLimintInterceptor implements HandlerInterceptor { @Resource private RedisTemplate<String,Object> redisTemplate; public static final String LOCK_PREFIX = "LOCK"; public static final String COUNT_PREFIX = "COUNT"; public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { if (handler instanceof HandlerMethod) { HandlerMethod targetMethod = (HandlerMethod) handler; AccessLimit targetClassAnnotation = targetMethod.getMethod().getDeclaringClass().getAnnotation(AccessLimit.class); boolean isBrushForAllInterface = false; long second = 0L, maxTime = 0L, forbiddenTime = 0L; if (targetClassAnnotation != null) { isBrushForAllInterface = true; second = targetClassAnnotation.second(); maxTime = targetClassAnnotation.maxTime(); forbiddenTime = targetClassAnnotation.forbiddenTime(); } AccessLimit accessLimit = targetMethod.getMethodAnnotation(AccessLimit.class); if (accessLimit != null) { second = accessLimit.second(); maxTime = accessLimit.maxTime(); forbiddenTime = accessLimit.forbiddenTime(); if (isForbindden(second, maxTime, forbiddenTime, ip, uri)) { throw new CommonException(ResultCode.ACCESS_FREQUENT); } } else { if (isBrushForAllInterface && isForbindden(second, maxTime, forbiddenTime, ip, uri)) { throw new CommonException(ResultCode.ACCESS_FREQUENT); } } } return true; } private boolean isForbindden(long second, long maxTime, long forbiddenTime, String ip, String uri) { /* same logic as before */ } }Time‑logic issue: The article discusses the sliding‑window problem where using a single Redis key with expiration does not represent a true “x seconds within any window” count, potentially allowing bursts that should be blocked.
Path‑parameter problem: Using only the raw URI as a key fails when the same endpoint has variable path parameters, because different parameter values produce different URIs. The suggested fix is to replace the URI with a stable identifier such as the controller class name plus method name.
Real IP acquisition: It notes that request.getRemoteAddr() may return the proxy’s IP instead of the client’s real IP, and suggests extracting the client IP from headers when behind a reverse proxy.
Summary: The author reflects on the learning journey, linking rate‑limiting implementation to broader concepts like custom annotations, reflection, singleton patterns, thread safety, and the Java concurrency ecosystem.
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.
Architecture Digest
Focusing on Java backend development, covering application architecture from top-tier internet companies (high availability, high performance, high stability), big data, machine learning, Java architecture, and other popular fields.
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.
