SoFunction
Updated on 2025-04-14

Five solutions for SpringBoot to implement interface flash prevention

1. Annotation-based access frequency limit

The most common anti-brushing solution is to achieve access frequency limit through custom annotations and AOP sections. This method is simple and easy to use and has low cost.

Implementation steps

1.1 Create a current limit annotation

@Target({})
@Retention()
@Documented
public @interface RateLimit {
    /**
      * Limited time period, unit in seconds
      */
    int time() default 60;
    
    /**
      * Maximum number of requests allowed in the limited time period
      */
    int count() default 10;
    
    /**
      * Current-limited key, supports SpEL expressions
      */
    String key() default "";
    
    /**
      * Prompt message
      */
    String message() default "The operation is too frequent, please try again later";
}

1.2 Implement current limiting section

@Aspect
@Component
@Slf4j
public class RateLimitAspect {
    
    @Autowired
    private StringRedisTemplate redisTemplate;
    
    @Around("@annotation(rateLimit)")
    public Object around(ProceedingJoinPoint pjp, RateLimit rateLimit) throws Throwable {
        // Get the method name of the request        String methodName = ().getName();
        // Get the requested class name        String className = ().getClass().getName();
        
        // Combined current limit key        String limitKey = getLimitKey(pjp, rateLimit, methodName, className);
        
        // Get current limiting parameters        int time = ();
        int count = ();
        
        // Execute current limiting logic        boolean limited = isLimited(limitKey, time, count);
        if (limited) {
            throw new RuntimeException(());
        }
        
        // Execute the target method        return ();
    }
    
    private String getLimitKey(ProceedingJoinPoint pjp, RateLimit rateLimit, String methodName, String className) {
        // Get user-defined key        String key = ();
        
        if ((key)) {
            // Support SpEL expression parsing            StandardEvaluationContext context = new StandardEvaluationContext();
            MethodSignature signature = (MethodSignature) ();
            String[] parameterNames = ();
            Object[] args = ();
            
            for (int i = 0; i < ; i++) {
                (parameterNames[i], args[i]);
            }
            
            ExpressionParser parser = new SpelExpressionParser();
            Expression expression = (key);
            key = (context, );
        } else {
            // Use class name + method name + IP address as key by default            HttpServletRequest request = ((ServletRequestAttributes) ()).getRequest();
            String ip = getIpAddress(request);
            key = ip + ":" + className + ":" + methodName;
        }
        
        return "rate_limit:" + key;
    }
    
    private boolean isLimited(String key, int time, int count) {
        // Use Redis' counter to achieve current limit        try {
            Long currentCount = ().increment(key, 1);
            
            // If it is the first time you access it, set the expiration time            if (currentCount == 1) {
                (key, time, );
            }
            
            return currentCount > count;
        } catch (Exception e) {
            ("current limit exception", e);
            return false;
        }
    }
    
    private String getIpAddress(HttpServletRequest request) {
        String ip = ("X-Forwarded-For");
        if (ip == null || () == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = ("Proxy-Client-IP");
        }
        if (ip == null || () == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = ("WL-Proxy-Client-IP");
        }
        if (ip == null || () == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = ("HTTP_CLIENT_IP");
        }
        if (ip == null || () == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = ("HTTP_X_FORWARDED_FOR");
        }
        if (ip == null || () == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = ();
        }
        return ip;
    }
}

1.3 Use examples

@RestController
@RequestMapping("/api")
public class UserController {
    
    @RateLimit(time = 60, count = 3, message = "Requests are too frequent, please try again later")
    @GetMapping("/user/{id}")
    public User getUser(@PathVariable Long id) {
        return (id);
    }
    
    // Specify key using SpEL expression    @RateLimit(time = 60, count = 1, key = "#id + '_' + #")
    @PostMapping("/user/{id}/update")
    public Result updateUser(@PathVariable Long id, @RequestBody UserDTO userDTO, HttpServletRequest request) {
        return (id, userDTO);
    }
}

Pros and cons analysis

advantage:

  • Simple implementation, easy to get started, and can remove Redis and replace local cache in a stand-alone situation.
  • Use annotated, non-invasive business code
  • Can accurately control the interface granularity
  • Supports flexible current limiting policy configuration

shortcoming:

  • Current limiting logic is relatively simple and cannot cope with complex scenarios
  • Lack of early warning mechanism

2. Token bucket algorithm implements current limit

The token bucket algorithm is a more flexible current limiting algorithm that allows burst traffic while limiting long-term average traffic.

Implementation steps

2.1 Introducing dependencies

The Guava library provided by Google contains the token bucket implementation:

<dependency>
    <groupId></groupId>
    <artifactId>guava</artifactId>
    <version>31.1-jre</version>
</dependency>

2.2 Create a token bucket current limiter

@Component
public class RateLimiter {
    // Use ConcurrentHashMap to store token buckets for different interfaces    private final ConcurrentHashMap&lt;String, &gt; rateLimiterMap = new ConcurrentHashMap&lt;&gt;();
    
    /**
      * Get the token bucket for a specific interface, if it does not exist, it will be created.
      * @param key Current limit key
      * @param permitsPerSecond The amount of requests allowed per second
      * @return Token bucket instance
      */
    public  getRateLimiter(String key, double permitsPerSecond) {
        return (key, 
            k -&gt; (permitsPerSecond));
    }
    
    /**
      * Try to get the token
      * @param key Current limit key
      * @param permitsPerSecond The amount of requests allowed per second
      * @param timeout timeout
      * @param unit time unit
      * @return Whether it is successful
      */
    public boolean tryAcquire(String key, double permitsPerSecond, long timeout, TimeUnit unit) {
         rateLimiter = getRateLimiter(key, permitsPerSecond);
        return (1, timeout, unit);
    }
}

2.3 Creating an interceptor

@Component
public class TokenBucketInterceptor implements HandlerInterceptor {
    
    @Autowired
    private RateLimiter rateLimiter;
    
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // Only limit the current to API requests        String requestURI = ();
        if (!("/api/")) {
            return true;
        }
        
        // Get the IP address as the current limit key        String ip = getIpAddress(request);
        String key = ip + ":" + requestURI;
        
        // Try to get the token, set the rate of 2 requests per second, and wait for 100 milliseconds        boolean acquired = (key, 2.0, 100, );
        
        if (!acquired) {
            // Failed to obtain, return the current limit response            ("application/json;charset=UTF-8");
            (HttpStatus.TOO_MANY_REQUESTS.value());
            ().write("{"code":429,"message":"Requests are too frequent, please try again later"}");
            return false;
        }
        
        return true;
    }
    
    // getIpAddress method is the same as above}

2.4 Configure the interceptor

@Configuration
public class WebConfig implements WebMvcConfigurer {
    
    @Autowired
    private TokenBucketInterceptor tokenBucketInterceptor;
    
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        (tokenBucketInterceptor)
                .addPathPatterns("/**");
    }
}

Pros and cons analysis

advantage:

  • Support burst traffic and will not completely reject short-term peaks
  • Smooth current limiting effect, better user experience
  • Different current limiting policies for different interfaces can be configured
  • No additional storage facilities required

shortcoming:

  • Only suitable for stand-alone deployment, distributed environments require additional transformation
  • Lost status after restarting the application
  • Unable to accurately control the total number of requests in the time window

3. Distributed current limit (Redis + Lua script)

For distributed systems, a stand-alone current limiting solution is difficult to meet the needs. Redis and Lua scripts can be used to achieve efficient distributed current limiting.

Implementation steps

3.1 Defining Lua Scripts

Create a Redis stream-limited Lua script and place it in the resources directoryscripts/rate_limiter.lua

-- Current limitKey
local key = KEYS[1]
-- Current limit窗口,Unit seconds
local window = tonumber(ARGV[1])
-- Current limit阈值
local threshold = tonumber(ARGV[2])
-- Current timestamp
local now = tonumber(ARGV[3])

-- Remove expired request history
('ZREMRANGEBYSCORE', key, 0, now - window * 1000)

-- Get the number of requests in the current window
local count = ('ZCARD', key)

-- If the number of requests exceeds the threshold,Reject request
if count &gt;= threshold then
    return 0
end

-- Add the current request record
('ZADD', key, now, now .. '-' .. ())
-- Set expiration time
('EXPIRE', key, window)

-- Returns the remaining number of available requests in the current window
return threshold - count - 1

3.2 Create Redis stream limiting service

@Service
@Slf4j
public class RedisRateLimiterService {
    
    @Autowired
    private StringRedisTemplate redisTemplate;
    
    private DefaultRedisScript&lt;Long&gt; rateLimiterScript;
    
    @PostConstruct
    public void init() {
        // Load Lua script        rateLimiterScript = new DefaultRedisScript&lt;&gt;();
        (new ClassPathResource("scripts/rate_limiter.lua"));
        ();
    }
    
    /**
      * Try to obtain access
      * @param key Current limit key
      * @param window time window (seconds)
      * @param threshold
      * @return The remaining number of available requests, -1 means that the current limit is
      */
    public long isAllowed(String key, int window, int threshold) {
        try {
            // Execute lua script            List&lt;String&gt; keys = (key);
            Long remainingCount = (
                rateLimiterScript, 
                keys, 
                (window), 
                (threshold),
                (())
            );
            
            return remainingCount == null ? -1 : remainingCount;
        } catch (Exception e) {
            ("Redis rate limiter error", e);
            // Release request when an exception occurs            return threshold;
        }
    }
}

3.3 Create distributed current limit annotations

@Target({})
@Retention()
@Documented
public @interface DistributedRateLimit {
    /**
      * Current limit key prefix
      */
    String prefix() default "rate:";
    
    /**
      * Time window, unit seconds
      */
    int window() default 60;
    
    /**
      * Maximum number of requests allowed in the time window
      */
    int threshold() default 10;
    
    /**
      * Current limit mode: ip - current limit by IP, user - current limit by user, all - overall interface current limit
      */
    String mode() default "ip";
}

3.4 Implement distributed current limiting section

@Aspect
@Component
@Slf4j
public class DistributedRateLimitAspect {
    
    @Autowired
    private RedisRateLimiterService rateLimiterService;
    
    @Autowired(required = false)
    private HttpServletRequest request;
    
    @Around("@annotation(rateLimit)")
    public Object around(ProceedingJoinPoint pjp, DistributedRateLimit rateLimit) throws Throwable {
        String key = generateKey(pjp, rateLimit);
        
        long remainingCount = (
            key, 
            (), 
            ()
        );
        
        if (remainingCount &lt; 0) {
            throw new RuntimeException("The interface is accessed too frequently, please try again later");
        }
        
        // Execute the target method        return ();
    }
    
    private String generateKey(ProceedingJoinPoint pjp, DistributedRateLimit rateLimit) {
        String methodName = ().getName();
        String className = ().getClass().getName();
        StringBuilder key = new StringBuilder(());
        
        (className).append(".").append(methodName);
        
        // Add different suffixes according to the current limit mode        switch (()) {
            case "ip":
                // Limit current by IP                (":").append(getIpAddress());
                break;
            case "user":
                // Limit the current by user                Object userId = getUserId();
                (":").append(userId != null ? userId : "anonymous");
                break;
            case "all":
                // The interface is generally limited in current, without adding suffixes                break;
            default:
                (":").append(getIpAddress());
                break;
        }
        
        return ();
    }
    
    private String getIpAddress() {
        // IP acquisition method is the same as above        if (request == null) {
            return "unknown";
        }
        // The code to get the IP is the same as the previous example        return "127.0.0.1"; // Simplify processing    }
    
    // Get the current user ID and implement it according to the actual authentication system    private Object getUserId() {
        // Here is a simplified process, which should be obtained from the authentication information in practice.        // For example: ().getAuthentication().getPrincipal()        return null;
    }
}

3.5 Use examples

@RestController
@RequestMapping("/api")
public class PaymentController {
    
    @DistributedRateLimit(prefix = "pay:", window = 3600, threshold = 5, mode = "user")
    @PostMapping("/payment")
    public Result createPayment(@RequestBody PaymentRequest paymentRequest) {
        // Create payment business logic        return (paymentRequest);
    }
    
    @DistributedRateLimit(window = 60, threshold = 30, mode = "ip")
    @GetMapping("/products")
    public List&lt;Product&gt; getProducts() {
        // Query product list        return ();
    }
    
    @DistributedRateLimit(window = 1, threshold = 100, mode = "all")
    @GetMapping("/hot/resource")
    public Resource getHotResource() {
        // Get popular resources        return ();
    }
}

Pros and cons analysis

advantage:

  • Suitable for distributed systems, sharing current limiting state among multiple instances
  • Supports multiple current limiting modes: by IP, user, total interface, etc.
  • Based on sliding window, more accurate counting
  • Use Lua scripts to ensure atomicity and avoid race conditions

shortcoming:

  • Reliable on Redis
  • Highly complex implementation

4. Integrated Sentinel to achieve interface flash protection

Alibaba's open source Sentinel is a powerful flow control component that provides rich functions such as current limiting, fuse, and system protection.

Implementation steps

4.1 Add dependencies

<dependency>
    <groupId></groupId>
    <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
    <version>2021.0.4.0</version>
</dependency>

4.2 Configuring Sentinel

existAdd configuration in:

# Sentinel console address=localhost:8080
# Cancel Sentinel console lazy loading=true
# Application name=my-application

4.3 Create Sentinel Configuration

@Configuration
public class SentinelConfig {
    
    @Bean
    public SentinelResourceAspect sentinelResourceAspect() {
        return new SentinelResourceAspect();
    }
    
    @PostConstruct
    public void init() {
        // Define flow control rules        initFlowRules();
    }
    
    private void initFlowRules() {
        List&lt;FlowRule&gt; rules = new ArrayList&lt;&gt;();
        
        // Set flow control rules for /api/user interface        FlowRule userRule = new FlowRule();
        ("/api/user");
        (RuleConstant.FLOW_GRADE_QPS); // Current limit based on QPS        (10); // 10 requests per second        (userRule);
        
        // Set flow control rules for /api/order interface        FlowRule orderRule = new FlowRule();
        ("/api/order");
        (RuleConstant.FLOW_GRADE_QPS);
        (5); // 5 requests per second        (RuleConstant.CONTROL_BEHAVIOR_WARM_UP); // Preheat mode        (10); // 10 seconds warm-up period        (orderRule);
        
        // Loading rules        (rules);
    }
}

4.4 Creating a URL resource parser

@Component
public class UrlCleaner implements RequestOriginParser {
    
    @Override
    public String parseOrigin(HttpServletRequest request) {
        // Get the URL path to the request        String path = ();
        
        // More complex parsing logic can be added, for example:        // 1. Remove path variables: /api/user/123 -> /api/user/{id}        // 2. Add the request method prefix: GET:/api/user        
        return path;
    }
}

4.5 Creating a global exception handler

@RestControllerAdvice
public class SentinelExceptionHandler {
    
    @ExceptionHandler()
    public Result handleBlockException(BlockException e) {
        String message = "Requests are too frequent, please try again later";
        if (e instanceof FlowException) {
            message = "Interface current limit:" + message;
        } else if (e instanceof DegradeException) {
            message = "Service downgrade: The system is busy, please try again later";
        } else if (e instanceof ParamFlowException) {
            message = "Hotspot parameter current limit: requests are too frequent";
        } else if (e instanceof SystemBlockException) {
            message = "System Protection: Insufficient System Resources";
        } else if (e instanceof AuthorityException) {
            message = "Authorization Control: No Access";
        }
        
        return (429, message);
    }
}

4.6 Annotation with @SentinelResource

@RestController
@RequestMapping("/api")
public class UserController {
    
    // Use resource names to define flow-limited resources    @SentinelResource(value = "getUserById", 
                      blockHandler = "getUserBlockHandler",
                      fallback = "getUserFallback")
    @GetMapping("/user/{id}")
    public User getUser(@PathVariable Long id) {
        return (id);
    }
    
    // Current limiting processing method    public User getUserBlockHandler(Long id, BlockException e) {
        ("Get user request blocked: {}", id, e);
        throw new RuntimeException("The request frequency is too high, please try again later");
    }
    
    // Exception fallback method    public User getUserFallback(Long id, Throwable t) {
        ("Get user failed: {}", id, t);
        User fallbackUser = new User();
        (id);
        ("Unknown");
        return fallbackUser;
    }
}

4.7 More complex configuration of current limiting rules

@Service
@Slf4j
public class SentinelRuleService {
    
    public void initComplexFlowRules() {
        List&lt;FlowRule&gt; rules = new ArrayList&lt;&gt;();
        
        // Current limiting rules based on QPS + call relationship        FlowRule apiRule = new FlowRule();
        ("/api/data");
        (RuleConstant.FLOW_GRADE_QPS);
        (20);
        
        // Restrict the source of call        ("frontend"); // Only restrict calls from front-end applications        
        // Flow control policy: associated resources        (RuleConstant.STRATEGY_RELATE);
        ("/api/important"); // When the important interface QPS is high, restrict the data interface        
        (apiRule);
        
        // Current limiting based on the number of concurrent threads        FlowRule threadRule = new FlowRule();
        ("/api/heavy-task");
        (RuleConstant.FLOW_GRADE_THREAD); // Based on the number of threads        (5); // Up to 5 threads are processed simultaneously        (threadRule);
        
        // Loading rules        (rules);
    }
    
    public void initHotspotRules() {
        // Hot spot parameter current limit rules        List&lt;ParamFlowRule&gt; rules = new ArrayList&lt;&gt;();
        
        ParamFlowRule rule = new ParamFlowRule("/api/product");
        // Perform current limiting on the 0th parameter (productId)        (0);
        (5);
        
        // Special case configuration        ParamFlowItem item1 = new ParamFlowItem();
        ("1"); // ProductId = 1        (10);  // There can be higher QPS        
        ParamFlowItem item2 = new ParamFlowItem();
        ("2"); // ProductId = 2        (2);   // More stringent restrictions        
        ((item1, item2));
        
        (rule);
        (rules);
    }
}

Pros and cons analysis

advantage:

  • Comprehensive functions, support QPS current limit, concurrent thread current limit, hot-spot parameter current limit, etc.
  • Supports multiple control strategies: direct rejection, warm-up, queue, etc.
  • Provides visual management of console
  • Support dynamic rule adjustment
  • Seamless integration with Spring Cloud system

shortcoming:

  • The learning curve is steep
  • In distributed scenarios, additional configuration rules need to be persisted
  • Introduced additional dependencies

5. Verification code and behavior analysis to prevent brushing

For certain sensitive operations (such as login, registration, payment, etc.), verification codes and behavioral analysis can be combined to prevent malicious requests.

Implementation steps

5.1 Graphic verification code implementation

First add dependencies:

<dependency>
    <groupId></groupId>
    <artifactId>easy-captcha</artifactId>
    <version>1.6.2</version>
</dependency>

5.2 Create a verification code service

@Service
public class CaptchaService {
    
    @Autowired
    private StringRedisTemplate redisTemplate;
    
    private static final long CAPTCHA_EXPIRE_TIME = 5 * 60; // 5 minutes    
    /**
      * Generate verification code
      * @param request HTTP request
      * @param response HTTP response
      * @return Verification code Base64 string
      */
    public String generateCaptcha(HttpServletRequest request, HttpServletResponse response) {
        // Generate verification code        SpecCaptcha captcha = new SpecCaptcha(130, 48, 5);
        
        // Generate verification code ID        String captchaId = ().toString();
        
        //Save the verification code into Redis        ().set(
            "captcha:" + captchaId, 
            ().toLowerCase(), 
            CAPTCHA_EXPIRE_TIME, 
            
        );
        
        // Set cookies        Cookie cookie = new Cookie("captchaId", captchaId);
        ((int) CAPTCHA_EXPIRE_TIME);
        ("/");
        (cookie);
        
        // Return the Base64 encoded verification code picture        return captcha.toBase64();
    }
    
    /**
      * Verification verification code
      * @param request HTTP request
      * @param captchaCode The verification code entered by the user
      * @return Whether the verification is passed
      */
    public boolean validateCaptcha(HttpServletRequest request, String captchaCode) {
        // Get the verification code ID from the cookie        Cookie[] cookies = ();
        String captchaId = null;
        
        if (cookies != null) {
            for (Cookie cookie : cookies) {
                if ("captchaId".equals(())) {
                    captchaId = ();
                    break;
                }
            }
        }
        
        if (captchaId == null) {
            return false;
        }
        
        // Get the correct verification code from Redis        String key = "captcha:" + captchaId;
        String correctCode = ().get(key);
        
        // Delete the verification code after successful verification        if (correctCode != null &amp;&amp; (())) {
            (key);
            return true;
        }
        
        return false;
    }
}

5.3 Create a verification code controller

@RestController
@RequestMapping("/api/captcha")
public class CaptchaController {
    
    @Autowired
    private CaptchaService captchaService;
    
    @GetMapping
    public Map<String, String> getCaptcha(HttpServletRequest request, HttpServletResponse response) {
        String base64 = (request, response);
        return ("captcha", base64);
    }
}

5.4 Creating verification code annotations

@Target({})
@Retention()
@Documented
public @interface CaptchaRequired {
    String captchaParam() default "captchaCode";
}

5.5 Implement verification code interceptor

@Component
public class CaptchaInterceptor implements HandlerInterceptor {
    
    @Autowired
    private CaptchaService captchaService;
    
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        if (!(handler instanceof HandlerMethod)) {
            return true;
        }
        
        HandlerMethod handlerMethod = (HandlerMethod) handler;
        CaptchaRequired captchaRequired = ();
        
        if (captchaRequired == null) {
            return true;
        }
        
        // Get verification code parameters        String captchaParam = ();
        String captchaCode = (captchaParam);
        
        if ((captchaCode)) {
            // Verification verification code            boolean valid = (request, captchaCode);
            if (valid) {
                return true;
            }
        }
        
        // Verification failed        ("application/json;charset=UTF-8");
        (HttpStatus.BAD_REQUEST.value());
        ().write("{"code":400,"message":"Verification code is wrong or expired"}");
        return false;
    }
}

5.6 Creating a behavioral analysis service

@Service
@Slf4j
public class BehaviorAnalysisService {
    
    @Autowired
    private StringRedisTemplate redisTemplate;
    
    /**
      * Check for suspicious machine behavior
      * @param request HTTP request
      * @return Is it suspicious
      */
    public boolean isSuspicious(HttpServletRequest request) {
        // 1. Obtain client information        String ip = getIpAddress(request);
        String userAgent = ("User-Agent");
        String requestId = ().getId();
        
        // 2. Check the access frequency        String freqKey = "behavior:freq:" + ip;
        Long count = ().increment(freqKey, 1);
        (freqKey, 1, );
        
        if (count != null &amp;&amp; count &gt; 30) {
            ("Access frequency abnormality: IP={}, count={}", ip, count);
            return true;
        }
        
        // 3. Check User-Agent        if (userAgent == null || isBotuserAgent(userAgent)) {
            ("SuspiciousUser-Agent: {}", userAgent);
            return true;
        }
        
        // 4. Check the request time mode        String timeKey = "behavior:time:" + ip;
        long now = ();
        String lastTimeStr = ().get(timeKey);
        
        if (lastTimeStr != null) {
            long lastTime = (lastTimeStr);
            long interval = now - lastTime;
            
            // If the request interval is very uniform, it may be a robot            if (isUniformInterval(ip, interval)) {
                ("The request interval is extremely uniform: IP={}, interval={}", ip, interval);
                return true;
            }
        }
        
        ().set(timeKey, (now), 10, );
        
        // More advanced detection logic...        
        return false;
    }
    
    /**
      * Check if it is a robot UA
      */
    private boolean isBotuserAgent(String userAgent) {
        String ua = ();
        return ("bot") || ("spider") || ("crawl") ||
               () || () &lt; 40;
    }
    
    /**
      * Check whether the request interval is abnormally uniform
      */
    private boolean isUniformInterval(String ip, long interval) {
        String key = "behavior:intervals:" + ip;
        
        // Get the most recent intervals        List&lt;String&gt; intervalStrs = ().range(key, 0, 4);
        ().leftPush(key, (interval));
        ().trim(key, 0, 9);  // Only the last 10        (key, 10, );
        
        if (intervalStrs == null || () &lt; 5) {
            return false;
        }
        
        // Calculate the variance of the interval. Small variance means that the request interval is very uniform        List&lt;Long&gt; intervals = ()
                .map(Long::parseLong)
                .collect(());
        
        double mean = ().mapToLong(Long::longValue).average().orElse(0);
        double variance = ()
                .mapToDouble(i -&gt; (i - mean, 2))
                .average()
                .orElse(0);
        
        return variance &lt; 100;  // The variance threshold needs to be adjusted according to actual situation    }
    
    // getIpAddress method is the same as above}

5.7 Creating a behavioral analysis interceptor

@Component
public class BehaviorAnalysisInterceptor implements HandlerInterceptor {
    
    @Autowired
    private BehaviorAnalysisService behaviorAnalysisService;
    
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // Check for endpoints that need protection        String path = ();
        if (("/api/") &amp;&amp; isPotentialRiskEndpoint(path)) {
            boolean suspicious = (request);
            
            if (suspicious) {
                // Verification code or other additional verification is required                ("application/json;charset=UTF-8");
                (HttpStatus.TOO_MANY_REQUESTS.value());
                ().write("{"code":429,"message":"Exception access was detected, please verify","needCaptcha":true}");
                return false;
            }
        }
        
        return true;
    }
    
    /**
      * Determine whether it is a high-risk endpoint
      */
    private boolean isPotentialRiskEndpoint(String path) {
        return ("/login") || 
               ("/register") || 
               ("/payment") || 
               ("/order") ||
               ("/password");
    }
}

5.8 Example of usage

@RestController
@RequestMapping("/api")
public class UserController {
    
    @CaptchaRequired
    @PostMapping("/login")
    public Result login(@RequestParam String username, 
                        @RequestParam String password,
                        @RequestParam String captchaCode) {
        // Login logic        return (username, password);
    }
    
    @CaptchaRequired
    @PostMapping("/register")
    public Result register(@RequestBody UserRegisterDTO registerDTO,
                          @RequestParam String captchaCode) {
        // Registration logic        return (registerDTO);
    }
}

Pros and cons analysis

advantage:

  • Can effectively distinguish between human users and automated scripts
  • It has a strong blocking effect on malicious users
  • Provides additional layer of security for sensitive operations
  • Adaptive security policies can be implemented

shortcoming:

  • Increases user operation costs and may affect user experience
  • Complex implementation requires front-end and back-end cooperation
  • Some verification codes may be cracked by OCR technology
  • Behavior analysis may cause misjudgment

Solution comparison and selection

plan Difficulty to achieve Anti-brush effect Distributed support User Experience Applicable scenarios
Annotation-based access frequency limit Low middle Need to cooperate with Redis generally General interface, simple scenario
Token bucket algorithm middle Medium-high Single machine good Scenarios that allow burst traffic
Distributed current limit (Redis+Lua) high high support generally Distributed system, accurate current limit
Sentinel Medium-high high Requires additional configuration Configurable Complex systems, multi-dimensional protection
Verification code and behavioral analysis high high support Poor Sensitive operations, critical business

Summarize

Interface anti-brushing is a systematic project that requires consideration of many factors: security, user experience, performance overhead and operation and maintenance complexity. The five solutions introduced in this article have their own advantages and disadvantages, and can be flexibly selected and combined according to actual needs.

No matter which solution is adopted, the following principles should be followed:

  • Minimum impact principle: try not to affect the normal user experience
  • Gradient protection principle: adopt protective measures of different strengths according to the importance of the interface
  • Monitorable principle: provide sufficient monitoring and alarm mechanism
  • Flexible adjustment principle: Support dynamic adjustment of protection parameters and strategies

By rationally implementing the interface anti-brush strategy, the security and stability of the system can be effectively improved and a better service experience can be provided to users.

The above is the detailed content of the five solutions for SpringBoot to implement interface anti-brushing. For more information about SpringBoot interface anti-brushing, please pay attention to my other related articles!