Skip to main content

Overview

The RateLimitKeyResolver interface resolves a stable rate limit key for an invocation. The key identifies which bucket or counter to charge against, enabling different limiting strategies like per-user, per-IP, or per-endpoint. Package: io.github.v4runsharma.ratelimiter.key Source: RateLimitKeyResolver.java:11

Purpose

The returned key is what the RateLimiter charges against. Different resolvers enable:
  • Per-user rate limiting
  • Per-IP rate limiting
  • Per-API-key rate limiting
  • Per-endpoint rate limiting
  • Custom composite keys

Methods

resolveKey

String resolveKey(RateLimitContext context)
Computes a key identifying the caller or resource bucket. Guidelines:
  • Return a non-empty, stable string (same caller → same key)
  • Prefer predictable formats (e.g., “user:123”, “ip:203.0.113.10”)
  • Consider including scope prefix for clarity
context
RateLimitContext
required
The invocation context containing the annotation, method, target class, and arguments.
return
String
A stable, non-empty key string identifying the rate limit bucket.

Usage examples

Per-user key resolver

import io.github.v4runsharma.ratelimiter.key.RateLimitKeyResolver;
import io.github.v4runsharma.ratelimiter.core.RateLimitContext;
import org.springframework.security.core.context.SecurityContextHolder;

public class UserKeyResolver implements RateLimitKeyResolver {
    
    @Override
    public String resolveKey(RateLimitContext context) {
        // Get user from security context
        String username = SecurityContextHolder.getContext()
            .getAuthentication()
            .getName();
        
        // Return user-scoped key
        return "user:" + username;
    }
}

Per-IP key resolver

import io.github.v4runsharma.ratelimiter.key.RateLimitKeyResolver;
import io.github.v4runsharma.ratelimiter.core.RateLimitContext;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

public class IpKeyResolver implements RateLimitKeyResolver {
    
    @Override
    public String resolveKey(RateLimitContext context) {
        ServletRequestAttributes attrs = 
            (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        
        if (attrs != null) {
            HttpServletRequest request = attrs.getRequest();
            String ip = getClientIp(request);
            return "ip:" + ip;
        }
        
        return "ip:unknown";
    }
    
    private String getClientIp(HttpServletRequest request) {
        String ip = request.getHeader("X-Forwarded-For");
        if (ip == null || ip.isEmpty()) {
            ip = request.getRemoteAddr();
        } else {
            // Take first IP in X-Forwarded-For chain
            ip = ip.split(",")[0].trim();
        }
        return ip;
    }
}

Parameter-based key resolver

import io.github.v4runsharma.ratelimiter.key.RateLimitKeyResolver;
import io.github.v4runsharma.ratelimiter.core.RateLimitContext;
import java.lang.reflect.Parameter;

public class ParameterKeyResolver implements RateLimitKeyResolver {
    
    @Override
    public String resolveKey(RateLimitContext context) {
        // Find parameter annotated with @RateLimitKey
        Parameter[] parameters = context.getMethod().getParameters();
        Object[] arguments = context.getArguments();
        
        for (int i = 0; i < parameters.length; i++) {
            if (parameters[i].isAnnotationPresent(RateLimitKey.class)) {
                Object value = arguments[i];
                return "param:" + value.toString();
            }
        }
        
        // Fallback to method-based key
        return "method:" + context.getMethod().getName();
    }
}

API key resolver

import io.github.v4runsharma.ratelimiter.key.RateLimitKeyResolver;
import io.github.v4runsharma.ratelimiter.core.RateLimitContext;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

public class ApiKeyResolver implements RateLimitKeyResolver {
    
    @Override
    public String resolveKey(RateLimitContext context) {
        ServletRequestAttributes attrs = 
            (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        
        if (attrs != null) {
            HttpServletRequest request = attrs.getRequest();
            String apiKey = request.getHeader("X-API-Key");
            
            if (apiKey != null && !apiKey.isEmpty()) {
                return "apikey:" + apiKey;
            }
        }
        
        return "apikey:anonymous";
    }
}

Composite key resolver

import io.github.v4runsharma.ratelimiter.key.RateLimitKeyResolver;
import io.github.v4runsharma.ratelimiter.core.RateLimitContext;

public class CompositeKeyResolver implements RateLimitKeyResolver {
    
    @Override
    public String resolveKey(RateLimitContext context) {
        String scope = context.getAnnotation().scope();
        String methodKey = context.getTargetClass().getSimpleName() 
            + "." + context.getMethod().getName();
        
        // Combine scope and method into composite key
        return scope + ":" + methodKey;
    }
}

Configuring custom resolver

You can specify a custom resolver in the @RateLimit annotation:
import io.github.v4runsharma.ratelimiter.annotation.RateLimit;

public class UserController {
    
    @RateLimit(
        limit = 100,
        duration = 1,
        keyResolver = UserKeyResolver.class
    )
    public void getUserProfile(String userId) {
        // Method implementation
    }
}
Or configure it globally as a Spring bean:
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class RateLimitConfig {
    
    @Bean
    public RateLimitKeyResolver rateLimitKeyResolver() {
        return new UserKeyResolver();
    }
}