SpringBoot定义拦截器 自定义注解 Redis 实现接口防刷 (限流)

闪耀之星喵 2024-06-08 ⋅ 48 阅读

引言

随着互联网的快速发展,接口防刷(限流)成为了一个十分重要的安全措施。在SpringBoot中,我们可以通过定义拦截器、自定义注解和Redis来实现接口防刷功能。

什么是接口防刷(限流)?

接口防刷指的是对系统中的某个接口进行限制,防止用户在较短的时间内大量请求该接口,从而导致系统资源的浪费和性能下降。通过限制每个用户对某个接口的访问频率,可以有效控制系统的负载,并提高系统的稳定性和安全性。

实现步骤

1. 定义自定义注解

首先,我们需要定义一个自定义注解 @AccessLimit,用来标注需要限流的接口方法。

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AccessLimit {
    // 最大请求数量
    int maxRequest() default 10;
    // 限制周期(单位:秒)
    int period() default 60;
}

2. 实现拦截器

接下来,我们需要实现一个拦截器 AccessLimitInterceptor,用来对带有 @AccessLimit 注解的接口方法进行拦截。

public class AccessLimitInterceptor extends HandlerInterceptorAdapter {
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 获取接口方法的注解信息
        HandlerMethod handlerMethod = (HandlerMethod) handler;
        AccessLimit accessLimit = handlerMethod.getMethodAnnotation(AccessLimit.class);
        if (accessLimit != null) {
            // 获取用户的IP地址和请求路径
            String ip = getIpAddress(request);
            String uri = request.getRequestURI();
            // 生成访问限制的键名
            String key = "access_limit:" + uri + ":" + ip;
            // 获取限制周期和最大请求数量
            int period = accessLimit.period();
            int maxRequest = accessLimit.maxRequest();
          
            // 判断是否超出限制
            if (redisTemplate.hasKey(key)) {
                int count = (int) redisTemplate.opsForValue().get(key);
                if (count >= maxRequest) {
                    throw new RuntimeException("请求过于频繁,请稍后再试!");
                } else {
                    redisTemplate.opsForValue().increment(key, 1);
                }
            } else {
                redisTemplate.opsForValue().set(key, 1, period, TimeUnit.SECONDS);
            }
        }
        return true;
    }

    // 获取客户端的IP地址
    private String getIpAddress(HttpServletRequest request) {
        String xForwardedForHeader = request.getHeader("X-Forwarded-For");
        if (xForwardedForHeader == null) {
            return request.getRemoteAddr();
        } else {
            // 多次反向代理后会有多个IP值,第一个非unknown的IP是真实IP
            return xForwardedForHeader.split(",")[0].trim();
        }
    }
}

3. 注册拦截器

然后,我们需要在SpringBoot的配置文件中注册拦截器 AccessLimitInterceptor

@Configuration
public class InterceptorConfig extends WebMvcConfigurerAdapter {
    @Autowired
    private AccessLimitInterceptor accessLimitInterceptor;
    
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(accessLimitInterceptor);
    }
}

4. 使用自定义注解

最后,我们可以在需要限流的接口方法上添加 @AccessLimit 注解。

@RestController
public class TestController {
    @RequestMapping("/test")
    @AccessLimit(maxRequest = 10, period = 60)
    public String test() {
        return "Hello World!";
    }
}

总结

通过定义拦截器、自定义注解和Redis,我们可以很方便地实现接口防刷(限流)功能。这样可以有效避免用户对系统接口的过于频繁的请求,保障系统的稳定性和安全性。

希望本篇博客能帮助到你,如果有任何问题,请留言或者私信联系我。

谢谢阅读!

参考链接:SpringBoot定义拦截器+自定义注解+Redis实现接口防刷(限流)


全部评论: 0

    我有话说: