博客
关于我
SpringBoot 接口防刷(注解实现)
阅读量:583 次
发布时间:2019-03-11

本文共 5463 字,大约阅读时间需要 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():是否需要登录验证。
  • 如果需要登录,获取用户信息并构建唯一标识符。
  • 从 Redis 缓存中获取用户的访问次数。
  • 判断访问次数是否超过限制:
    • 如果是第一次访问,记录一次。
    • 如果在限制内,允许访问并记录最新次数。
    • 如果超过限制,返回错误信息。
  • 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();    }}

    Controller 注解应用

    在实际应用中,我们可以在需要防刷的接口上添加 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 应用中实现接口防刷功能。这种方式的优点包括:

  • 灵活性:可以对不同接口设置不同的访问限制。
  • 可扩展性:支持多种防刷策略,如时间限制和次数限制。
  • 非破坏性:不需要修改接口签名,适用于已有接口。
  • 易于部署:通过 Redis 缓存实现高性能和高可用性。
  • 这种解决方案在实际应用中表现良好,能够有效防止接口被恶意刷击,同时保持服务的正常运行。

    转载地址:http://sbytz.baihongyu.com/

    你可能感兴趣的文章
    Vue踩坑笔记 - 关于vue静态资源引入的问题
    查看>>
    Netty工作笔记0025---SocketChannel API
    查看>>
    Netty工作笔记0027---NIO 网络编程应用--群聊系统2--服务器编写2
    查看>>
    Netty工作笔记0050---Netty核心模块1
    查看>>
    Netty工作笔记0057---Netty群聊系统服务端
    查看>>
    Netty工作笔记0060---Tcp长连接和短连接_Http长连接和短连接_UDP长连接和短连接
    查看>>
    Netty工作笔记0063---WebSocket长连接开发2
    查看>>
    Netty工作笔记0070---Protobuf使用案例Codec使用
    查看>>
    Netty工作笔记0077---handler链调用机制实例4
    查看>>
    Netty工作笔记0084---通过自定义协议解决粘包拆包问题2
    查看>>
    Netty工作笔记0085---TCP粘包拆包内容梳理
    查看>>
    Netty常用组件一
    查看>>
    Netty常见组件二
    查看>>
    netty底层源码探究:启动流程;EventLoop中的selector、线程、任务队列;监听处理accept、read事件流程;
    查看>>
    Netty心跳检测机制
    查看>>
    Netty核心模块组件
    查看>>
    Netty框架内的宝藏:ByteBuf
    查看>>
    Netty框架的服务端开发中创建EventLoopGroup对象时线程数量源码解析
    查看>>
    Netty源码—2.Reactor线程模型一
    查看>>
    Netty源码—3.Reactor线程模型三
    查看>>