API 接口防刷

API 接口防刷
顾名思义,想让某个接口某个人在某段时间内只能请求N次。
在项目中比较常见的问题也有,那就是连点按钮导致请求多次,以前在web端有表单重复提交,可以通过token 来解决。
除了上面的方法外,前后端配合的方法。现在全部由后端来控制。
原理
在你请求的时候,服务器通过redis 记录下你请求的次数,如果次数超过限制就不给访问。
在redis 保存的key 是有时效性的,过期就会删除。
代码实现:
为了让它看起来逼格高一点,所以以自定义注解的方式实现

@RequestLimit 注解

import java.lang.annotation.*;

/**
 * 请求限制的自定义注解
 *
 * @Target 注解可修饰的对象范围,ElementType.METHOD 作用于方法,ElementType.TYPE 作用于类
 * (ElementType)取值有:
 *     1.CONSTRUCTOR:用于描述构造器
 *     2.FIELD:用于描述域
 *     3.LOCAL_VARIABLE:用于描述局部变量
 *     4.METHOD:用于描述方法
 *     5.PACKAGE:用于描述包
 *     6.PARAMETER:用于描述参数
 *     7.TYPE:用于描述类、接口(包括注解类型) 或enum声明
 * @Retention定义了该Annotation被保留的时间长短:某些Annotation仅出现在源代码中,而被编译器丢弃;
 * 而另一些却被编译在class文件中;编译在class文件中的Annotation可能会被虚拟机忽略,
 * 而另一些在class被装载时将被读取(请注意并不影响class的执行,因为Annotation与class在使用上是被分离的)。
 * 使用这个meta-Annotation可以对 Annotation的“生命周期”限制。
 * (RetentionPoicy)取值有:
 *     1.SOURCE:在源文件中有效(即源文件保留)
 *     2.CLASS:在class文件中有效(即class保留)
 *     3.RUNTIME:在运行时有效(即运行时保留)
 *
 * @Inherited
 * 元注解是一个标记注解,@Inherited阐述了某个被标注的类型是被继承的。
 * 如果一个使用了@Inherited修饰的annotation类型被用于一个class,则这个annotation将被用于该class的子类。
 */
@Documented
@Inherited
@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface RequestLimit {
    // 在 second 秒内,最大只能请求 maxCount 次
    int second() default 1;
    int maxCount() default 1;
}

RequestLimitIntercept 拦截器
自定义一个拦截器,请求之前,进行请求次数校验

import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import top.lrshuai.limit.annotation.RequestLimit;
import top.lrshuai.limit.common.ApiResultEnum;
import top.lrshuai.limit.common.Result;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.reflect.Method;
import java.util.concurrent.TimeUnit;

/**
 * 请求拦截
 */
@Slf4j
@Component
public class RequestLimitIntercept extends HandlerInterceptorAdapter {

    @Autowired
    private RedisTemplate<String,Object> redisTemplate;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        /**
         * isAssignableFrom() 判定此 Class 对象所表示的类或接口与指定的 Class 参数所表示的类或接口是否相同,或是否是其超类或超接口
         * isAssignableFrom()方法是判断是否为某个类的父类
         * instanceof关键字是判断是否某个类的子类
         */
        if(handler.getClass().isAssignableFrom(HandlerMethod.class)){
            //HandlerMethod 封装方法定义相关的信息,如类,方法,参数等
            HandlerMethod handlerMethod = (HandlerMethod) handler;
            Method method = handlerMethod.getMethod();
            // 获取方法中是否包含注解
            RequestLimit methodAnnotation = method.getAnnotation(RequestLimit.class);
            //获取 类中是否包含注解,也就是controller 是否有注解
            RequestLimit classAnnotation = method.getDeclaringClass().getAnnotation(RequestLimit.class);
            // 如果 方法上有注解就优先选择方法上的参数,否则类上的参数
            RequestLimit requestLimit = methodAnnotation != null?methodAnnotation:classAnnotation;
            if(requestLimit != null){
                if(isLimit(request,requestLimit)){
                    resonseOut(response,Result.error(ApiResultEnum.REQUST_LIMIT));
                    return false;
                }
            }
        }
        return super.preHandle(request, response, handler);
    }
    //判断请求是否受限
    public boolean isLimit(HttpServletRequest request,RequestLimit requestLimit){
        // 受限的redis 缓存key ,因为这里用浏览器做测试,我就用sessionid 来做唯一key,如果是app ,可以使用 用户ID 之类的唯一标识。
        String limitKey = request.getServletPath()+request.getSession().getId();
        // 从缓存中获取,当前这个请求访问了几次
        Integer redisCount = (Integer) redisTemplate.opsForValue().get(limitKey);
        if(redisCount == null){
            //初始 次数
            redisTemplate.opsForValue().set(limitKey,1,requestLimit.second(), TimeUnit.SECONDS);
        }else{
            if(redisCount.intValue() >= requestLimit.maxCount()){
                return true;
            }
            // 次数自增
            redisTemplate.opsForValue().increment(limitKey);
        }
        return false;
    }

    /**
     * 回写给客户端
     * @param response
     * @param result
     * @throws IOException
     */
    private void resonseOut(HttpServletResponse response, Result result) throws IOException {
        response.setCharacterEncoding("UTF-8");
        response.setContentType("application/json; charset=utf-8");
        PrintWriter out = null ;
        String json = JSONObject.toJSON(result).toString();
        out = response.getWriter();
        out.append(json);
    }
}

拦截器写好了,但是还得添加注册

WebMvcConfig 配置类
因为我的是Springboot2. 所以只需实现WebMvcConfigurer
如果是springboot1.
那就继承自 WebMvcConfigurerAdapter
然后重写addInterceptors() 添加自定义拦截器即可。

@Slf4j
@Component
public class WebMvcConfig implements WebMvcConfigurer {

    @Autowired
    private RequestLimitIntercept requestLimitIntercept;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        log.info("添加拦截");
        registry.addInterceptor(requestLimitIntercept);
    }
}

Controller
控制层测试接口,
使用方式:

第一种:直接在类上使用注解@RequestLimit(maxCount = 5,second = 1)

第二种:在方法上使用注解@RequestLimit(maxCount = 5,second = 1)

maxCount 最大的请求数、second 代表时间,单位是秒

默认1秒内,每个接口只能请求一次

@RestController
@RequestMapping("/index")
@RequestLimit(maxCount = 5,second = 1)
public class IndexController {

    /**
     * @RequestLimit 修饰在方法上,优先使用其参数
     * @return
     */
    @GetMapping("/test1")
    @RequestLimit
    public Result test(){
        //TODO ...
        return Result.ok();
    }

    /**
     * @RequestLimit 修饰在类上,用的是类的参数
     * @return
     */
    @GetMapping("/test2")
    public Result test2(){
        //TODO ...
        return Result.ok();
    }
}

如果在类和方法上同时有@RequestLimit注解 ,以方法上的参数为准,好像注释有点多了。

原文地址:https://blog.51cto.com/13981400/2393248

时间: 2024-10-10 17:57:01

API 接口防刷的相关文章

最近公司网站一直给人刷验证码,以及API接口。

最近这几天公司网站一直给人刷验证码,以及API接口.虽然可以使用iptables把它ip封了,但是那些人都是半夜来刷的,真可恶,同时有好几十个肉鸡过来刷,并且有时候有几个ip居然用iptables封不了,这几天疯狂了解相关的DDOS攻击原理信息,看能否找到有效的方法拦截.

解密微信域名防封API接口实现原理

微信域名防封是指通过技术手段来实现预付措施.一切说自己完全可以防封的那都是不可能的.一切说什么免死域名不会死的那也是吹牛逼的.我们要做的是让我们的推广域名寿命更长一点,成本更低一点,效果更好一点. 第一:微信域名检测+微信域名切换 你需要有一个微信域名检测接口,自己开发或是购买都可以.配置好你的接口请求程序,准备2套域名A和B.比如说分享出去的域名是A,这里面A被称作是主域名.点开后跳到B,跳转之前检测一下B有没有被封,这里面的B就称作是落地域名.通常情况下落地域名B需要准备几十甚至上百个,利用

2020最新的新浪短网址API接口分享-防封短网址如何生成分享

在短信,微信,微博,等等各大营销平台中我们都能看到http://t.cn/xxxx样式的新浪短链接,这种超短链接很好的满足了营销中要求链接简洁,美观,专业的特性,而且通过短网址包装后的链接对外发布也起到了防封的效果.那么新浪t.cn短链接如何生成呢?网址缩短API接口如何调用?今天我就给大家分享一下.   新浪t.cn短网址API接口: http://qingmeidwz.cn/shorten.php?url_long=http://www.baidu.com   微信url.cn短网址API接

微信域名防封杀状态查询api接口—提供微信域名是否被封状态查询 准确率100%

微信域名检测api接口,我相信很多做微信营销的用户都在使用,但是经常碰到接口不稳定,域名特殊问题检查不出来,比如重定向报蓝,非官方网站等.那么今天给大家分享一个非常稳定,且可检测出网站无法访问报红,重定向,非官方网站,复制链接浏览器打开的四大域名问题的接口.   微信域名检测api接口: http://www.xiaohejc.cn/check_api.php?url_long=http://www.baidu.com 使用说明: 将api接口地址中 "http://www.baidu.com&

通过注解实现接口限流防刷

采用注解的方式 1)定义一个注解 @Retention(RUNTIME) @Target(METHOD) public @interface AccessLimit { int seconds(); int maxCount(); } 2)在需要限流的方法前加这样的注解 @AccessLimit(seconds=5, maxCount=5) 3)在拦截器里进行判断,看方法是否使用了AccessLimit注解修饰 @Component public class AccessInterceptor

各开放平台API接口通用SDK序列文章 前言

最近两年一直在做API接口相关的工作,在平时工作中以及网上看到很多刚接触API接口调用的新人一开始会感到很不适应,要看的文档一大堆,自己要调用的接口找不着,或都找着了不知道怎么去调用,记得包括自己刚开始做API接口调用的相关工作时,也是比较抓狂的,也是硬着头皮去看各种文档,熟悉代码,在网上不断地去查找资料.所以决定写一序列文章把之前做的API接口相关的工作做个总结,二来写一个通用SDK把之前涉及到的代码封装成类库,以便以后可以更好地使用.就不用再重创轮子了,三来对一些有需要的朋友,比如刚接触AP

API的防重

说说API的防重放机制 2017-03-20 18:19 by 轩脉刃, 685 阅读, 7 评论, 收藏, 编辑 说说API的防重放机制 我们在设计接口的时候,最怕一个接口被用户截取用于重放攻击.重放攻击是什么呢?就是把你的请求原封不动地再发送一次,两次...n次,一般正常的请求都会通过验证进入到正常逻辑中,如果这个正常逻辑是插入数据库操作,那么一旦插入数据库的语句写的不好,就有可能出现多条重复的数据.一旦是比较慢的查询操作,就可能导致数据库堵住等情况. 这里就有一种防重放的机制来做请求验证.

atitit.基于http json api 接口设计 最佳实践 总结o7

atitit.基于http  json  api 接口设计 最佳实践 总结o7 1. 需求:::serverand android 端接口通讯 2 2. 接口开发的要点 2 2.1. 普通參数 meth,param, 2 2.2. 全部的參数定义 2 2.3. key,dynami key)韩式 static key? 2 2.4. 防篡改 sign 2 2.5. Encry加密 3 2.6. zip压缩:: 3 2.7. 首先压缩韩式加密??? 3 3. 选型大全:rim ,ws, http 

使用过滤器对mvc api接口安全加密

asp.net api接口安全 安全要求: b1.防伪装攻击(案例:在公共网络环境中,第三方 有意或恶意 的调用我们的接口) b2.防篡改攻击(案例:在公共网络环境中,请求头/查询字符串/内容 在传输过程被修改) b3.防重放攻击(案例:在公共网络环境中,请求被截获,稍后被重放或多次重放) b4.防数据信息泄漏(案例:截获用户登录请求,截获到账号.密码等) 设计原则 1.轻量级 2.适合于异构系统(跨操作系统.多语言简易实现) 3.易于开发 4.易于测试 5.易于部署 6.满足接口安全需求(满足