通过Interceptor以及Redis实现接口访问防刷

方式一

以下是通过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;

    @Autowired
    public RateLimiterInterceptor(RedisTemplate<String, String> redisTemplate) {
    
    
        this.redisTemplate = redisTemplate;
    }

    @Override
    public 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;

    @Autowired
    public WebConfig(RateLimiterInterceptor rateLimiterInterceptor) {
    
    
        this.rateLimiterInterceptor = rateLimiterInterceptor;
    }

    @Override
    public 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;

    @Override
    public 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;
    }

    @Override
    public 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;
    }

    @Override
    public 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响应。

猜你喜欢

转载自blog.csdn.net/weixin_47763579/article/details/129839455