方式一
以下是通过Interceptor以及Redis实现接口访问防刷的Java代码示例:
首先,创建一个自定义拦截器类,实现HandlerInterceptor接口,并在preHandle方法中添加接口防刷逻辑。例如:
@Component
public class RateLimiterInterceptor implements HandlerInterceptor {private static final String RATE_LIMITER_KEY_PREFIX = "rate_limiter:";private static final int MAX_REQUESTS_PER_SECOND = 10;private final RedisTemplate<String, String> redisTemplate;@Autowiredpublic RateLimiterInterceptor(RedisTemplate<String, String> redisTemplate) {this.redisTemplate = redisTemplate;}@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {String ip = request.getRemoteAddr();String key = RATE_LIMITER_KEY_PREFIX + ip;Long count = redisTemplate.opsForValue().increment(key, 1);if (count == 1) {// Set a TTL to the key so it gets deleted after one second.redisTemplate.expire(key, 1, TimeUnit.SECONDS);}if (count > MAX_REQUESTS_PER_SECOND) {response.setStatus(HttpStatus.TOO_MANY_REQUESTS.value());return false;}return true;}
}
上述代码中,我们使用了Redis作为存储桶,每个IP地址对应一个key,每一次请求就将key中的计数器加一,并设置一个过期时间为1秒钟。如果计数器超过最大请求数,则返回HTTP 429 Too Many Requests响应。
接下来,在Spring Boot应用程序中配置拦截器,例如:
@Configuration
public class WebConfig implements WebMvcConfigurer {private final RateLimiterInterceptor rateLimiterInterceptor;@Autowiredpublic WebConfig(RateLimiterInterceptor rateLimiterInterceptor) {this.rateLimiterInterceptor = rateLimiterInterceptor;}@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(rateLimiterInterceptor);}
}
这样,我们就可以实现接口访问防刷功能了。每个IP地址在1秒钟内最多只能发送10个请求。如果超过限制,则返回HTTP 429响应。
方式二
以下是通过Interceptor以及Redis 自定义注解 + 反射 实现接口访问防刷的Java代码示例:
首先,创建一个自定义注解@RateLimit,并在注解中添加限制参数。例如:
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RateLimit {int limit() default 10;int timeout() default 1;
}
然后,在服务启动时,使用反射扫描所有带有@RateLimit注解的方法,并生成对应的拦截器和key。例如:
@Component
public class RateLimiterBeanPostProcessor implements BeanPostProcessor, ApplicationContextAware {private final Map<Method, String> rateLimiterMap = new HashMap<>();private ApplicationContext applicationContext;@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {Class<?> clazz = bean.getClass();Method[] methods = clazz.getDeclaredMethods();for (Method method : methods) {RateLimit annotation = AnnotationUtils.findAnnotation(method, RateLimit.class);if (annotation != null) {int limit = annotation.limit();int timeout = annotation.timeout();String key = "rate_limiter:" + clazz.getName() + ":" + method.getName() + ":" +Arrays.toString(method.getParameterTypes());RedisTemplate<String, String> redisTemplate = applicationContext.getBean(RedisTemplate.class);RateLimiterInterceptor rateLimiterInterceptor = new RateLimiterInterceptor(redisTemplate, key, limit, timeout);this.rateLimiterMap.put(method, key);WebMvcConfigurer appConfig = (WebMvcConfigurer) applicationContext.getBean(WebMvcConfigurer.class);appConfig.addInterceptors(rateLimiterInterceptor);}}return bean;}@Overridepublic void setApplicationContext(ApplicationContext applicationContext) throws BeansException {this.applicationContext = applicationContext;}
}
上述代码中,我们通过注解扫描所有带有@RateLimit注解的方法,并在应用程序启动时生成对应的拦截器和key。这里使用了RedisTemplate将key与计数器存储到Redis中。
接下来,创建一个自定义拦截器类RateLimiterInterceptor,并在preHandle方法中添加接口防刷逻辑。其中,根据之前生成的key从Redis中获取对应的计数器并进行限制。例如:
public class RateLimiterInterceptor implements HandlerInterceptor {private final RedisTemplate<String, String> redisTemplate;private final String key;private final int limit;private final int timeout;public RateLimiterInterceptor(RedisTemplate<String, String> redisTemplate, String key, int limit, int timeout) {this.redisTemplate = redisTemplate;this.key = key;this.limit = limit;this.timeout = timeout;}@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {String ip = request.getRemoteAddr();String realKey = key + ":" + ip;Long count = redisTemplate.opsForValue().increment(realKey, 1);redisTemplate.expire(realKey, timeout, TimeUnit.SECONDS);if (count > limit) {response.setStatus(HttpStatus.TOO_MANY_REQUESTS.value());return false;}return true;}
}
最后,在需要限制访问的接口方法上添加@RateLimit注解,例如:
@RestController
public class HelloController {@RateLimit(limit = 5, timeout = 1)@GetMapping("/hello")public String hello() {return "Hello World";}
}
这样,我们就可以实现接口访问防刷功能了。每个IP地址在1秒钟内最多只能发送5个请求。如果超过限制,则返回HTTP 429响应。