您当前的位置:首页 > 电脑百科 > 程序开发 > 框架

SpringBoot 这样实现API接口请求防刷,代码非常的优雅!

时间:2023-04-24 13:40:41  来源:  作者:程序猿怪咖

 

1 为什么要做接口防刷?

如果你的服务器应用,被一些人而已攻击,写入脚本不停的刷服务端的某一个接口,这样服务端的压力聚会非常的,甚至可能给服务端带来灾难,如果是涉及支付相关的服务那就更加损失惨重。因此我们就可以做一个接口防刷的功能,如果服务端接收到了某一个用户端请求数在某个时段超过了我们设定的数量就可以直接不让其访问了,也就做到了接口防刷。

 

2 API接口防刷原理

原理其实很简单,在服务端记录客户端的请求次数,如果客户端请求数量超过设置的请求数就直接不让访问了。

 

3 SpringBoot 实现API接口防刷

为了让我们的代码非常优雅,这里采用自定义注解的方式来实现,同时用redis来记录客户端请求次数。

3.1 自定义API防刷注解

下面是自定义防止客户端而已刷接口的注解。

代码示例如下:

package com.test.merservice.controller.manage;

import JAVA.lang.annotation.*;

/**
 * API防刷自定义注解(设置默认每秒只能请求1次)
 */
@Documented
@Inherited
@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface RequestAntiBrushing {
    int second() default 1;//时间范围内(秒)
    int maxCount() default 1;//最大请求书
}

3.2 自定义API防刷请求拦截器

自定义拦截器来完成API防刷,其主要功能逻辑就是在请求之前拦截请求,验证请求数是否超过设定的限制。

diama.NETic示例如下:

package com.test.merservice.controller.manage;

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 javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.reflect.Method;

/**
 * 客户端API防刷拦截器
 */
@Slf4j
@Component
public class RequestAntiBrushingIntercept extends HandlerInterceptorAdapter {

    @Autowired
    private RedisTemplate<String,Object> redisTemplate;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        /**
         * instanceof关键字是判断是否某个类的子类
         */
        if(handler.getClass().isAssignableFrom(HandlerMethod.class)){//isAssignableFrom()判定此 Class 对象所表示的类或接口与指定的 Class 参数所表示的类或接口是否相同,或是否是其超类或超接口(isAssignableFrom()方法是判断是否为某个类的父类)
            //HandlerMethod 封装方法定义相关的信息,如类,方法,参数等
            HandlerMethod handlerMethod = (HandlerMethod) handler;
            Method method = handlerMethod.getMethod();
            // 获取方法中是否包含注解
            RequestAntiBrushing methodAnnotation = method.getAnnotation(RequestAntiBrushing.class);
            //获取 类中是否包含注解,也就是controller 是否有注解
            RequestAntiBrushing classAnnotation = method.getDeclaringClass().getAnnotation(RequestAntiBrushing.class);
            // 如果 方法上有注解就优先选择方法上的参数,否则类上的参数
            RequestAntiBrushing 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,RequestAntiBrushing requestLimit){
        // 受限的redis 缓存key ,因为这里用浏览器做测试,我就用sessionid 来做唯一key,如果是App ,可以使用 用户ID 之类的唯一标识。
        String limitKey = request.getServletPath()+request.getSession().getId();
        // 从缓存中获取,当前这个请求访问了几次
        //Integer redisCount = (Integer) redisTemplate.opsForValue().get(limitKey);
      //下面取原子类,Integer本身是线程不安全的,避免并发问题
        RedisAtomicInteger redisCount = new RedisAtomicInteger(limitKey, this.redisTemplate.getConnectionFactory());
        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);
    }
}

3.3 自定义拦截器注册

package com.test.merservice.controller.manage;

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;

@Slf4j
@Component
public class WebMvcConfig implements WebMvcConfigurer {

    @Autowired
    private RequestAntiBrushingIntercept requestAntiBrushingIntercept;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        log.info("注册拦截");
        registry.addInterceptor(requestAntiBrushingIntercept);
    }
}

3.4 API防刷注解使用

注解默认是客户端每秒最大请求1次,这里注解设置为每秒最大请求3次。

package com.sllt.merservice.controller.manage;

import io.seata.core.model.Result;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/user/index")
public class IndexController {

    @PostMapping("/index")
    @RequestAntiBrushing(maxCount = 3,second = 1)
    public Result test(){
        //调用service层业务省略
        return Result.ok();
    }
}


Tags:SpringBoot   点击:()  评论:()
声明:本站部分内容及图片来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,如有任何标注错误或版权侵犯请与我们联系(Email:2595517585@qq.com),我们将及时更正、删除,谢谢。
▌相关推荐
1 为什么要做接口防刷?如果你的服务器应用,被一些人而已攻击,写入脚本不停的刷服务端的某一个接口,这样服务端的压力聚会非常的,甚至可能给服务端带来灾难,如果是涉及支付相关的...【详细内容】
2023-04-24  Tags: SpringBoot  点击:(0)  评论:(0)  加入收藏
今天要聊的就是「博客管理」中全文搜索的实现,基于 SpringBoot+Vue+ES 实现,先给大家看一下效果: 全文搜索+关键字高亮,是不是和百度的效果差不多,话不多说,直接聊如何实现。 该...【详细内容】
2023-04-15  Tags: SpringBoot  点击:(17)  评论:(0)  加入收藏
前言数据源,实际就是数据库连接池,负责管理数据库连接,在Springboot中,数据源通常以一个bean的形式存在于IOC容器中,也就是我们可以通过依赖注入的方式拿到数据源,然后再从数据源...【详细内容】
2023-04-12  Tags: SpringBoot  点击:(8)  评论:(0)  加入收藏
今天我们来讨论如何在项目开发中优雅地使用RocketMQ。本文分为三部分,第一部分实现SpringBoot与RocketMQ的整合,第二部分解决在使用RocketMQ过程中可能遇到的一些问题并解决...【详细内容】
2023-04-12  Tags: SpringBoot  点击:(38)  评论:(0)  加入收藏
前言最近业务部门接手了外包供应商的项目过来自己运维,该部门的小伙伴发现了一个问题,比如后端的DTO有个属性名为nPrice的字段,通过json渲染到前端后,变成nprice,而预期的字段是...【详细内容】
2023-04-04  Tags: SpringBoot  点击:(11)  评论:(0)  加入收藏
在 Spring Boot 中使用 Spring AOP 实现接口鉴权可以帮助我们对接口的调用进行权限控制。下面是一些常见的方法:1 基于注解的方法:在接口方法上添加自定义注解,通过定义切面类...【详细内容】
2023-03-30  Tags: SpringBoot  点击:(60)  评论:(0)  加入收藏
很早以前,我曾写过两篇介绍如何在SpringBoot中使用Guava和Redis实现接口限流的文章。具体包括: 使用Guava实现单机令牌桶限流 使用Redis实现分布式限流现在,一个问题摆在我们面...【详细内容】
2023-03-24  Tags: SpringBoot  点击:(27)  评论:(0)  加入收藏
在如今的关系型数据库中,有两个开源产品是你必须知道的。其中一个是MySQL,相信关注我的小伙伴们一定都不陌生,因为之前的Spring Boot关于关系型数据库的所有例子都是对MySQL来...【详细内容】
2023-03-23  Tags: SpringBoot  点击:(39)  评论:(0)  加入收藏
Spring MVC自动配置Spring Boot为Spring MVC提供了自动配置,可以在大多数应用程序中很好地工作。自动配置在Spring默认的基础上添加了以下特性: 包含ContentNegotiatingViewR...【详细内容】
2023-03-23  Tags: SpringBoot  点击:(31)  评论:(0)  加入收藏
在 Spring Boot 中,我们可以使用多种方式来实现枚举类型传递,以下是几个常规的方法:1 @RequestParam 和 @PathVariable 注解:可以将枚举类型作为方法的参数,然后使用 @RequestPar...【详细内容】
2023-03-21  Tags: SpringBoot  点击:(20)  评论:(0)  加入收藏
▌简易百科推荐
1 为什么要做接口防刷?如果你的服务器应用,被一些人而已攻击,写入脚本不停的刷服务端的某一个接口,这样服务端的压力聚会非常的,甚至可能给服务端带来灾难,如果是涉及支付相关的...【详细内容】
2023-04-24  程序猿怪咖    Tags:SpringBoot   点击:(0)  评论:(0)  加入收藏
这段代码展示了如何通过Java配置类的方式来配置MyBatis框架,其中涉及到数据源的配置、SqlSessionFactory的创建、SqlSessionTemplate的创建以及事务管理器的创建。@MapperSca...【详细内容】
2023-04-23  豆ok  今日头条  Tags:MyBatis   点击:(8)  评论:(0)  加入收藏
受益于开源技术的发展,以及响应快速开发的实际业务需求,跨平台开发不仅限于移动端跨平台,桌面端虽然在市场应用方面场景不像移动端那么丰富,但也有市场的需求。相对于个人开发者...【详细内容】
2023-04-19  艾特程序员  搜狐号  Tags:框架   点击:(10)  评论:(0)  加入收藏
抖音开源了一个前端UI框架-Semi-UI,还是不错的。大家可以了解下哈,等成熟了,可以用用。Semi-UI,现代、全面、灵活的设计系统和 UI 库。快速搭建美观的React 应用。github开源地...【详细内容】
2023-04-18  Meta     Tags:UI框架   点击:(17)  评论:(0)  加入收藏
Mor (发音为 /mɔːr/,类似 more) 是饿了么开发的一款基于小程序 DSL 的,可扩展的多端研发框架。...【详细内容】
2023-04-18    CSDN  Tags: MorJS   点击:(22)  评论:(0)  加入收藏
项目介绍一款基于开源框架实现的脚手架平台,帮助中小企业快速迭代开发。项目特点项目架构项目架构项目功能 会员管理 文档管理 菜单管理 用户管理 角色管理 系统日志 接口ap...【详细内容】
2023-04-17  睿智的仁杰  今日头条  Tags:脚手架   点击:(18)  评论:(0)  加入收藏
在 MyBatis 中,可以使用${}或#{}来拼接 SQL 语句中的参数。在进行模糊查询时,需要使用通配符 % 表示任意字符,具体写法如下: 使用${}:<select id="selectUsersByName" resultTyp...【详细内容】
2023-04-16      Tags:MyBatis   点击:(15)  评论:(0)  加入收藏
今天要聊的就是「博客管理」中全文搜索的实现,基于 SpringBoot+Vue+ES 实现,先给大家看一下效果: 全文搜索+关键字高亮,是不是和百度的效果差不多,话不多说,直接聊如何实现。 该...【详细内容】
2023-04-15  会踢球的程序源  今日头条  Tags:SpringBoot   点击:(17)  评论:(0)  加入收藏
前言 在日常开发中经常遇到运营审核经销商活动、任务等等类似业务需求,大部分需求中状态稳定且单一无需使用状态机,但是也会出现大量的if...else前置状态代码,也是不够那么的“...【详细内容】
2023-04-12  之家技术  微信公众号  Tags:Spring   点击:(18)  评论:(0)  加入收藏
Kyverno 是来自 Nirmata 的开源项目,后来捐赠给了 CNCF。Kyverno 是一个具有验证和变异能力的 Kubernetes 策略引擎,但是它还有生成资源的功能,还加入了 API 对象查询的能力。K...【详细内容】
2023-04-12  k8s技术圈  微信公众号  Tags:Kubernetes   点击:(16)  评论:(0)  加入收藏
站内最新
站内热门
站内头条