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

本文共 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():是否需要登录验证。
  • 如果需要登录,获取用户信息并构建唯一标识符。
  • 从 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;
    @Component
    public 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;
    @Component
    public 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;
    @Controller
    public 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/

    你可能感兴趣的文章
    NFS网络文件系统
    查看>>
    nft文件传输_利用remoting实现文件传输-.NET教程,远程及网络应用
    查看>>
    NFV商用可行新华三vBRAS方案实践验证
    查看>>
    ng build --aot --prod生成文件报错
    查看>>
    ng 指令的自定义、使用
    查看>>
    nghttp3使用指南
    查看>>
    Nginx
    查看>>
    nginx + etcd 动态负载均衡实践(三)—— 基于nginx-upsync-module实现
    查看>>
    nginx + etcd 动态负载均衡实践(二)—— 组件安装
    查看>>
    nginx + etcd 动态负载均衡实践(四)—— 基于confd实现
    查看>>
    Nginx + Spring Boot 实现负载均衡
    查看>>
    Nginx + uWSGI + Flask + Vhost
    查看>>
    Nginx - Header详解
    查看>>
    Nginx - 反向代理、负载均衡、动静分离、底层原理(案例实战分析)
    查看>>
    nginx 1.24.0 安装nginx最新稳定版
    查看>>
    nginx 301 永久重定向
    查看>>
    nginx css,js合并插件,淘宝nginx合并js,css插件
    查看>>
    Nginx gateway集群和动态网关
    查看>>
    Nginx Location配置总结
    查看>>
    Nginx log文件写入失败?log文件权限设置问题
    查看>>