SpringBoot API 接口防刷

这篇具有很好参考价值的文章主要介绍了SpringBoot API 接口防刷。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

接口防刷

接口防刷: 顾名思义,想让某个接口某个人在某段时间内只能请求N次。
在项目中比较常见的问题也有,那就是连点按钮导致请求多次,以前在web端有表单重复提交,可以通过token来解决。

除了上面的方法外,前后端配合的方法。现在全部由后端来控制。

接口防刷原理

在你请求的时候,服务器通过redis 记录下你请求的次数,如果次数超过限制就不给访问。
在redis 保存的key 是有时效性的,过期就会删除。

代码实现

通过自定义注解的方式实现

@RequestLimit 注解

package com.mry.springboottools.annotation;

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 拦截器

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

package com.mry.springboottools.interceptor;

import com.alibaba.fastjson.JSONObject;
import com.mry.springboottools.annotation.RequestLimit;
import com.mry.springboottools.common.ApiResultEnum;
import com.mry.springboottools.common.Result;
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 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() 添加自定义拦截器即可。

package com.mry.springboottools.config;

import com.mry.springboottools.interceptor.RequestLimitIntercept;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

/**
 * 因为我的是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秒内,每个接口只能请求一次。

package com.mry.springboottools.controller;

import com.mry.springboottools.annotation.RequestLimit;
import com.mry.springboottools.common.Result;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * 控制层测试接口,
 *
 * 使用方式:
 * 第一种:直接在类上使用注解@RequestLimit(maxCount = 5,second = 1)
 * 第二种:在方法上使用注解@RequestLimit(maxCount = 5,second = 1)
 *
 * maxCount 最大的请求数、second 代表时间,单位是秒
 * 默认1秒内,每个接口只能请求一次
 */
@RestController
@RequestMapping("/mry")
@RequestLimit(maxCount = 5,second = 1)
public class RequestLimitController {

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

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

如果在类和方法上同时有@RequestLimit注解 ,以方法上的参数为准。

验证

正常请求如下图:
SpringBoot API 接口防刷

触发接口防刷机制如下图:
SpringBoot API 接口防刷文章来源地址https://www.toymoban.com/news/detail-417453.html

到了这里,关于SpringBoot API 接口防刷的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 接口安全验证及防刷处理方案

    暴露在外网的api接口需要做到 防篡改 和 防重放 才能保证接口安全。 比如:短信验证接口,支付接口,请求时长超过1S以上的 防篡改 我们知道http 是一种无状态的协议,服务端并不知道客户端发送的请求是否合法,也并不知道请求中的参数是否正确。 举个例子, 现在有个充

    2024年01月16日
    浏览(36)
  • SpringBoot 统计更多Api接口日志信息

    Further Reading : SpringBoot 统计API接口用时该使用过滤器还是拦截器? 日志打印放afterCompletion是为了兼容异常场景也可以记录日志 配置文件可以配置是否开启统计

    2024年01月23日
    浏览(60)
  • 三方开放接口,Springboot通过AOP实现API接口的签名验证

    前言 对外开放的接口,需要验证请求方发送过来的数据确实是由发送方发起的,并且中途不能被篡改和伪造,所以才会对接口的访问进行签名验证,以保证双方获取到的原来的信息是没有经过篡改的。 实现方法 对请求的信息内容,通过MD5运算或者其他算法(必须是 不可逆的

    2024年02月07日
    浏览(51)
  • SpringBoot使用Swagger配置API接口文档

    Swagger是一个用于设计、构建和文档化 RESTful API 的开源框架。它提供了一组工具,使得开发人员能够更轻松地定义、描述和测试API接口。 具体来说,Swagger包含以下几个核心组件: Swagger规范(Swagger Specification): 定义了一种格式化的API规范,使用YAML或JSON格式,用于描述API的各

    2024年02月05日
    浏览(47)
  • 后端Springboot框架搭建APi接口开发(第一章)

    本文章以IDEA为开发工具,使用SSM框架进行项目编写 我们用一个简单的用户表进行操作演示 首先创建 Data 数据库 创建 User 数据表,表中包含 用户邮箱 , 用户姓名 , 用户密码 放点数据进去 打开IDEA,在右上角点击NewProject。创建新的项目 选择Spring Initializr框架,依次输入项目

    2023年04月08日
    浏览(42)
  • 加密无忧:SpringBoot中快速搭建安全的API接口

    在项目中,为了保证数据的安全,我们常常会对传递的数据进行加密。常用的加密算法包括对称加密(AES)和非对称加密(RSA),博主选取码云上最简单的API加密项目进行下面的讲解。 https://gitee.com/isuperag/rsa-encrypt-body-spring-boot 该项目使用RSA加密方式对API接口返回的数据加密

    2024年04月22日
    浏览(39)
  • Spring Security的API Key实现SpringBoot 接口安全

    Spring Security 提供了各种机制来保护我们的 REST API。其中之一是 API 密钥。API 密钥是客户端在调用 API 调用时提供的令牌。 在本教程中,我们将讨论如何在Spring Security中实现基于API密钥的身份验证。 API Keys 一些REST API使用API密钥进行身份验证。API密钥是一个标记,用于向API客户

    2024年03月15日
    浏览(54)
  • 基于SpringBoot 实现一个文件上传的API接口。并使用postman测试

    1.  创建实体类用于返回结果、  2. 定义文件上传接口以及实现类    3. service 业务层 4. controller 控制层    5. postman 测试   文章参考 链接SpringBoot实现文件上传接口-阿里云开发者社区 (aliyun.com)

    2024年02月12日
    浏览(73)
  • 快试试用 API Key 来保护你的 SpringBoot 接口安全吧!

    来源:baeldung.com/spring-boot-api-key-secret 安全性在REST API开发中扮演着重要的角色。一个不安全的REST API可以直接访问到后台系统中的敏感数据。因此,企业组织需要关注API安全性。 Spring Security 提供了各种机制来保护我们的 REST API。其中之一是 API 密钥。API 密钥是客户端在调用

    2024年02月13日
    浏览(34)
  • SpringBoot 统计API接口用时该使用过滤器还是拦截器?

    统计请求的处理时间(用时)既可以使用 Servlet 过滤器(Filter) ,也可以使用 Spring 拦截器(Interceptor) 。两者都可以在请求处理前后插入自定义逻辑,从而实现对请求响应时间的统计。 如果你需要在更底层、与框架无关的地方记录所有请求(包括静态资源请求)的处理时间

    2024年01月18日
    浏览(43)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包