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中,检查上次访问时间和当前时间的时间间隔,根据限制条件判断是否允许继续访问,超过限制则返回对应信息