博客
关于我
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/

    你可能感兴趣的文章
    Netpas:不一样的SD-WAN+ 保障网络通讯品质
    查看>>
    NetScaler的常用配置
    查看>>
    netsh advfirewall
    查看>>
    NETSH WINSOCK RESET这条命令的含义和作用?
    查看>>
    Netty WebSocket客户端
    查看>>
    netty 主要组件+黏包半包+rpc框架+源码透析
    查看>>
    Netty 异步任务调度与异步线程池
    查看>>
    Netty中集成Protobuf实现Java对象数据传递
    查看>>
    Netty事件注册机制深入解析
    查看>>
    Netty原理分析及实战(四)-客户端与服务端双向通信
    查看>>
    Netty客户端断线重连实现及问题思考
    查看>>
    Netty工作笔记0006---NIO的Buffer说明
    查看>>
    Netty工作笔记0007---NIO的三大核心组件关系
    查看>>
    Netty工作笔记0011---Channel应用案例2
    查看>>
    Netty工作笔记0013---Channel应用案例4Copy图片
    查看>>
    Netty工作笔记0014---Buffer类型化和只读
    查看>>
    Netty工作笔记0020---Selectionkey在NIO体系
    查看>>
    Vue踩坑笔记 - 关于vue静态资源引入的问题
    查看>>
    Netty工作笔记0025---SocketChannel API
    查看>>
    Netty工作笔记0027---NIO 网络编程应用--群聊系统2--服务器编写2
    查看>>