1. 自定义注解

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface RateLimit {

    //默认最大访问次数
    int value() default 3;

    //默认时间窗口(秒)
    long duration() default 60;
}

 

2. 创建拦截器处理频率逻辑

@Slf4j
public class RateLimitInterceptor implements HandlerInterceptor {

    private ConcurrentHashMap<String,Long> requestCountMap = new ConcurrentHashMap<>();

    @Override
    public boolean preHandle(@NotNull HttpServletRequest request, @NotNull HttpServletResponse response, @NotNull Object handler) throws Exception {
        if (handler instanceof HandlerMethod){
            HandlerMethod handlerMethod = (HandlerMethod) handler;
            RateLimit rateLimitAnnotation = handlerMethod.getMethod().getAnnotation(RateLimit.class);
            if (rateLimitAnnotation != null){
                long duration = rateLimitAnnotation.duration();
                int maxRequest = rateLimitAnnotation.value();
                String ipAddress = getClientIpAddress(request);
                String key = ipAddress + ":" + request.getRequestURI();

                //获取上一次请求的时间戳
                Long lastAccessTime = requestCountMap.getOrDefault(key, 0L);
                //获取当前请求的时间戳
                long currentAccessTime = System.currentTimeMillis();
                //计算时间间隔
                long timeInterval = TimeUnit.MILLISECONDS.toSeconds(currentAccessTime - lastAccessTime);
                //是否超过时间间隔
                if (timeInterval < duration){
                    //检查访问次数是否超过限制
                    if (requestCountMap.getOrDefault(key + "_count",0L) > maxRequest){
                        response.getWriter().write("访问频率超过限制");
                        response.setStatus(HttpServletResponse.SC_SERVICE_UNAVAILABLE);
                        return false;
                    }
                }else {
                    //重置访问时间和访问次数
                    requestCountMap.put(key,currentAccessTime);
                    requestCountMap.put(key + "_count",0L);
                }
                //访问次数 + 1
                requestCountMap.put(key + "_count",requestCountMap.getOrDefault(key + "_count",0L) + 1);
            }
        }
        return true;
    }

    private String getClientIpAddress(HttpServletRequest request) {
        // 获取客户端IP地址的方法实现,可以根据具体的需求自行实现
        // 例如,可以通过HttpServletRequest对象获取IP地址
        // return request.getRemoteAddr();
        return "127.0.0.1"; // 这里仅作示例,假设IP地址为本地地址
    }
}

 

3. 添加拦截器

@Configuration
public class MyConfig  implements WebMvcConfigurer {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new RateLimitInterceptor());
    }
}

 

4. 在接口上使用注解

    /**
     * 通过id查询项目任务书
     * @param id id
     * @return R
     */
    @Operation(summary = "通过id查询", description = "通过id查询")
    @GetMapping("/{id}" )
    @RateLimit
    public R getById(@PathVariable("id" ) String id) {
        return R.ok(prjAssignService.getByIdAssign(id));
    }

  在拦截器preHandler中,检查上次访问时间和当前时间的时间间隔,根据限制条件判断是否允许继续访问,超过限制则返回对应信息

 

posted on 2024-04-23 17:24  homle  阅读(97)  评论(0编辑  收藏  举报