本文共 5581 字,大约阅读时间需要 18 分钟。
我们定义了一个注解类 AccessLimit,用于限制接口的调用次数和防止恶意刷接口。该注解的主要字段包括:
seconds():接口有效时间,单位为秒。maxCount():最大允许调用次数。needLogin():是否需要登录验证(默认为 true)。import java.lang.annotation.Retention;import java.lang.annotation.Target;import static java.lang.annotation.ElementType.METHOD;import static java.lang.annotation.RetentionPolicy.RUNTIME;@Retention(RUNTIME)@Target(METHOD)public @interface AccessLimit { int seconds(); int maxCount(); boolean needLogin() default true;} 接下来,我们创建了一个 FangshuaInterceptor 拦截器,用于检查请求是否超过访问限制。拦截器通过以下步骤实现防刷功能:
HandlerMethod 是否存在 AccessLimit 注解。seconds():接口有效时间。maxCount():最大允许调用次数。needLogin():是否需要登录验证。import com.alibaba.fastjson.JSON;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Component;import org.springframework.web.method.HandlerMethod;import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.io.OutputStream;@Componentpublic class FangshuaInterceptor extends HandlerInterceptorAdapter { @Autowired private RedisService redisService; @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { if (handler instanceof HandlerMethod) { HandlerMethod hm = (HandlerMethod) handler; AccessLimit accessLimit = hm.getMethodAnnotation(AccessLimit.class); if (accessLimit == null) { return true; } int seconds = accessLimit.seconds(); int maxCount = accessLimit.maxCount(); boolean login = accessLimit.needLogin(); String key = request.getRequestURI(); if (login) { // 假设用户是动态获取的 userId key += "1"; } AccessKey ak = AccessKey.withExpire(seconds); Integer count = redisService.get(ak, key, Integer.class); if (count == null) { redisService.set(ak, key, 1); } else if (count < maxCount) { redisService.incr(ak, key); } else { render(response, CodeMsg.ACCESS_LIMIT_REACHED); return false; } } return true; } private void render(HttpServletResponse response, CodeMsg cm) throws Exception { response.setContentType("application/json;charset=UTF-8"); OutputStream out = response.getOutputStream(); String str = JSON.toJSONString(Result.error(cm)); out.write(str.getBytes("UTF-8")); out.flush(); out.close(); }} 在 Spring Boot 应用中,我们通过 WebConfig 注册拦截器:
import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Component;import org.springframework.web.method.HandlerMethod;import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.io.OutputStream;@Componentpublic class FangshuaInterceptor extends HandlerInterceptorAdapter { @Autowired private RedisService redisService; @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { if (handler instanceof HandlerMethod) { HandlerMethod hm = (HandlerMethod) handler; AccessLimit accessLimit = hm.getMethodAnnotation(AccessLimit.class); if (accessLimit == null) { return true; } int seconds = accessLimit.seconds(); int maxCount = accessLimit.maxCount(); boolean login = accessLimit.needLogin(); String key = request.getRequestURI(); if (login) { // 假设用户是动态获取的 userId key += "1"; } AccessKey ak = AccessKey.withExpire(seconds); Integer count = redisService.get(ak, key, Integer.class); if (count == null) { redisService.set(ak, key, 1); } else if (count < maxCount) { redisService.incr(ak, key); } else { render(response, CodeMsg.ACCESS_LIMIT_REACHED); return false; } } return true; } private void render(HttpServletResponse response, CodeMsg cm) throws Exception { response.setContentType("application/json;charset=UTF-8"); OutputStream out = response.getOutputStream(); String str = JSON.toJSONString(Result.error(cm)); out.write(str.getBytes("UTF-8")); out.flush(); out.close(); }} 在实际应用中,我们可以在需要防刷的接口上添加 AccessLimit 注解,并指定相关参数:
import org.springframework.stereotype.Controller;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.ResponseBody;@Controllerpublic class FangshuaController { @AccessLimit(seconds=5, maxCount=5, needLogin=true) @RequestMapping("/demo") @ResponseBody public Result fangshua() { return Result.success("请求成功"); }} 通过以上方案,我们可以轻松地在 Spring Boot 应用中实现接口防刷功能。这种方式的优点包括:
这种解决方案在实际应用中表现良好,能够有效防止接口被恶意刷击,同时保持服务的正常运行。
转载地址:http://sbytz.baihongyu.com/