SpringBoot如何对接口防刷限流处理

news2024/9/20 14:40:56

一、API防刷限流:

API接口限流,旨在预防用户过度频繁地访问特定接口,以及抵御潜在的恶意攻击行为,这些行为可能导致后端服务器承受过高的负载,进而引发内存资源紧张。为了有效缓解服务器面临的压力,确保服务的稳定性和可用性,对接口实施防刷限流措施显得尤为重要。

实现思路:

在Spring Boot应用中结合Redis实现后端拦截前端请求的功能,具体流程如下:后端系统拦截来自前端的每一个请求,使用请求的IP地址加上请求的具体信息(如接口路径、请求方法等)作为key,在Redis中查询对应的value值。若查询结果为空,表示这是该IP对该请求的首次访问,系统将继续执行后续操作,并在Redis中为该key设置一个初始的访问次数(如1)及过期时间。若查询结果不为空,则解析value值以获取当前的访问次数,并与预设的限定值进行比较。若访问次数超过限定值,则立即向前端返回提示信息,并终止执行后续操作;若未超过限定值,则更新Redis中的访问次数,并继续执行后续操作。

二、代码示例

第一步 创建AccessLimitIntercept类,编写实现功能的核心代码。

package com.example.mybatisdemo.config;

import io.swagger.models.auth.In;
import org.apache.commons.lang3.ObjectUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.TimeUnit;
import java.util.logging.Handler;

@Component

public class AccessLimitIntercept implements HandlerInterceptor {

  @Autowired
  private RedisTemplate redisTemplate;

  //限定访问次数
  private int count=5;
  //访问时间
  private int time=10;
 private final static String localIp = "127.0.0.1";
 /**
  * 获取ip地址
  * @param request
  * @return
  */
 public static String getIp(HttpServletRequest request){
     String ipAddress;
     try {
         ipAddress = request.getHeader("x-forwarded-for");
         if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
             ipAddress = request.getHeader("Proxy-Client-IP");
         }
         if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
             ipAddress = request.getHeader("WL-Proxy-Client-IP");
         }
         if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
             ipAddress = request.getRemoteAddr();
             if (localIp.equals(ipAddress)) {
                 // 根据网卡取本机配置的IP
                 InetAddress inet = null;
                 try {
                     inet = InetAddress.getLocalHost();
                     ipAddress = inet.getHostAddress();
                 } catch (UnknownHostException e) {
                     e.printStackTrace();
                 }
             }
         }
         // 对于通过多个代理的情况,第一个IP为客户端真实IP,多个IP按照','分割
         if (ipAddress != null && ipAddress.length() > 15) {
             // = 15
             if (ipAddress.indexOf(",") > 0) {
                 ipAddress = ipAddress.substring(0, ipAddress.indexOf(","));
             }
         }
     } catch (Exception e) {
         ipAddress = "";
     }
     return "0:0:0:0:0:0:0:1".equals(ipAddress) ? localIp : ipAddress;
 }

 /**
  * 进入controller前的预处理
  * @param request
  * @param response
  * @param handler
  * @return
  * @throws Exception
  */
 @Override
 public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
     
     String key=getIp(request)+request.getRequestURI();
     Object value = redisTemplate.opsForValue().get(key);
     // 获取redis的value
     Integer valueTime=null;
     //表示第一次访问
     if(value==null)
     {
       //存放访问的次数,time表示时间, TimeUnit.SECONDS表示时间单位为秒
       redisTemplate.opsForValue().set(key, 1, time, TimeUnit.SECONDS);
     }
     if(value!=null)
     {
         valueTime= (Integer) value;

         if(valueTime<count)
         {
             //访问次数累加
             redisTemplate.opsForValue().set(key, valueTime+1, time, TimeUnit.SECONDS);

         }else {
             output(response, "{\"code\":\"8002\",\"message\":\"请求过于频繁,请稍后再试\"}");
             return false;
         }

     }
     return true;
 }
 public void output(HttpServletResponse response, String msg) throws IOException {
     response.setContentType("application/json;charset=UTF-8");
     ServletOutputStream outputStream = null;
     try {
         outputStream = response.getOutputStream();
         outputStream.write(msg.getBytes(StandardCharsets.UTF_8));
     } catch (IOException e) {
         e.printStackTrace();
     } finally {
         if (ObjectUtils.isNotEmpty(outputStream)) {
             outputStream.flush();
             outputStream.close();
         }
     }
 }

 @Override
 public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
     HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
 }

 @Override
 public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
     HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
 }
}

配置配置文件,将写好的拦截器配置到容器中

package com.example.mybatisdemo.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
@EnableWebMvc
public class WebMvcConfig implements WebMvcConfigurer {

    /**
     * 这里需要先将限流拦截器入住,不然无法获取到拦截器中的redistemplate
     * @return
     */
    @Bean
    public AccessLimitIntercept getAccessLimitIntercept() {
        return new AccessLimitIntercept();
    }

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(getAccessLimitIntercept()).addPathPatterns("/**");
    }
}

创建 controller 测试类

@RestController
public class AcessController {


    @GetMapping("/test")

    public String Testmethod()
    {
        String st="hello,you enter system";
        System.out.println("成功访问到方法");
        return st;
    }

}

在浏览器访问 http://localhost:8080/test

连续点击测试

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2103469.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

数据结构代码集训day15(适合考研、自学、期末和专升本)

本份题目来自B站up&#xff1a;白话拆解数据结构 今日题目如下; &#xff08;1&#xff09;编写算法&#xff0c;实现十进制转十六进制&#xff1b; &#xff08;2&#xff09;汉诺塔&#xff08;Hanoi Tower&#xff09;&#xff0c;又称河内塔&#xff0c;源于印度一个古老…

javascript网页设计案例,非常详细

这里我将为你提供一个详细的JavaScript网页设计案例。我们将创建一个简单的动态网页&#xff0c;包含一个可以显示当前时间的时钟和一个可以切换背景颜色的按钮。 1. HTML部分 首先&#xff0c;我们需要创建一个HTML文件来定义网页的基本结构。 <!DOCTYPE html> <h…

Python ppt

python生成ppt&#xff0c;数据源为html from lxml import html from pptx import Presentation from pptx.util import Inches from pptx.dml.color import RGBColor from pptx.enum.shapes import MSO_SHAPE# HTML代码 html_content """ <html><b…

【深度学习 transformer】使用pytorch 训练transformer 模型,hugginface 来啦

Hugging Face是一个致力于开源自然语言处理&#xff08;NLP&#xff09;和机器学习项目的社区。它由几个关键组件组成&#xff1a; Transformers&#xff1a;这是一个基于PyTorch的库&#xff0c;提供了各种预训练的NLP模型&#xff0c;如BERT、GPT、RoBERTa、DistilBERT等。它…

【陪诊系统-PC管理端】动态路由

先说说这里为什么要使用动态路由&#xff1f; 因为前面的菜单管理功能模块中&#xff0c;可以创建或修改不同权限&#xff0c;当前登录账号可以绑定不同的权限&#xff0c;不同权限能访问的功能页面不同&#xff0c;所以使用动态路由来控制。 而登录成功后&#xff0c;服务器…

Sentence-BERT实现文本匹配【对比损失函数】

引言 还是基于Sentence-BERT架构&#xff0c;或者说Bi-Encoder架构&#xff0c;但是本文使用的是参考2中提出的对比损失函数。 架构 如上图&#xff0c;计算两个句嵌入 u \pmb u u和 v \pmb v v​之间的距离(1-余弦相似度)&#xff0c;然后使用参考2中提出的对比损失函数作为…

docker 安装 rabbitmq

参考文档&#xff1a; https://hub.docker.com/_/rabbitmq/ https://www.rabbitmq.com/docs/download https://www.kuangstudy.com/zl/rabbitmq#1366643532940484610 执行下面的命令 docker run -d -it --name myrabbit -e RABBITMQ_DEFAULT_USERadmin -e RABBITMQ_DEFAULT_PA…

“解决 Docker 启动失败:排查和修复 overlay2 存储驱动与网络模块问题”。

目录 1.报错如下 2.报错详解 1. ” 表明 overlay2 存储驱动挂载失败&#xff0c;找不到相应设备。 2.表明在路径中找不到 fuse-overlayfs 可执行文件。 3.表明加载 bridge 和 br_netfilter 模块失败。 4.及后续一系列关于停止服务的信息&#xff0c;是由于前面的错误导致的…

硬件生产厂家运维系统思路

当前硬件生产厂家运维已经逐渐摆脱原有的现场调试&#xff0c;初步诊断和运维已经进化为远程运维&#xff1b;主要方式为厂家建立运维系统&#xff0c;使用人员只需要关注厂家公众号或者登录官网&#xff0c;即可完成原来必须到现场才能解决的问题&#xff1b; 原弊端&#xff…

探讨 | 大模型在传统NLP任务的使用姿势

写在前面 今天给大家带来一篇震宇兄&#xff08;知乎邱震宇&#xff09;探讨大模型技术在提升传统NLP类任务效果上的应用方式的文章&#xff0c;主要从文本分类任务出发。 知乎&#xff1a;https://zhuanlan.zhihu.com/p/704983302PS&#xff1a;长文警告&#xff01;建议收藏…

Burp Suite Professional 2024.8 发布下载,新增功能概览

Burp Suite Professional 2024.8 (macOS, Linux, Windows) - Web 应用安全、测试和扫描 Burp Suite Professional, Test, find, and exploit vulnerabilities. 请访问原文链接&#xff1a;https://sysin.org/blog/burp-suite-pro/&#xff0c;查看最新版。原创作品&#xff0…

退火吗?C#/WinForm演示退火算法

退火模型&#xff1a;模拟退火算法&#xff08;Simulated Annealing, SA&#xff09;是一种概率型全局优化算法&#xff0c;灵感来源于物理学中的退火过程。它通过模拟金属退火过程中的加热和缓慢冷却&#xff0c;来寻找问题的近似全局最优解。算法开始时&#xff0c;初始温度设…

70万个哺乳动物功能基因集!这个数据库值得重视

生信碱移 Rummagene数据库 Rummagene 从 PubMed Central (PMC) 出版物提取了超70万个基因集&#xff0c;用于各类基因功能关联注释。 组学技术的引入逐渐将生物和生物医学研究从研究单个基因和蛋白质转向研究基因集、基因簇、分子复合物和基因表达模块。许多生物医学和生物研究…

log4j 清除MDC上下文 MDC分类日志

在项目里需要分类收集处理日志信息&#xff0c;使用 log4j的MDC在线程中添加分类信息。不过最近却出现日志信息记录错误的情况&#xff0c;具体来说&#xff0c;就是会出现本来是属于下一个分类的一部分信息莫名的记录到上一个分类的日志文件中了。这很显然是MDC信息错误造成的…

【nnUNet】环境安装

很高兴在雪易的CSDN遇见你 VTK技术爱好者 QQ&#xff1a;870202403 公众号&#xff1a;VTK忠粉 前言 本文分享医疗分割模型nnUNet的环境安装教程&#xff0c;希望对各位小伙伴有所帮助&#xff01; 感谢各位小伙伴的点赞关注&#xff0c;小易会继续努力分享&#xff0…

数据中台 | 数据智能平台产品系列文章,企业开发和盘活数据资产的利器!(上篇)

引言 发展数字经济&#xff0c;实现数字中国是我国的国家战略&#xff0c;坚定且不可动摇&#xff0c;近期随着《数据二十条》、《“数据要素”三年行动计划》、《关于加强数据资产管理的指导意见》等重磅政策的发布&#xff0c;使数字化转型成为越来越多企业增强竞争、扩大营收…

LabVIEW程序员错误排查思路

当LabVIEW程序员在开发过程中遇到难以解决的错误且网上搜不到答案时&#xff0c;需要采取系统性的方法进行排查和解决。这包括回顾代码逻辑、深入理解LabVIEW的底层机制、参考专业文献和求助社区等方式。下面将从多角度详细解读专业程序员在面对这种困境时的应对策略&#xff0…

【系统分析师】-面向对象方法

目录 1、基本概念 2、UML 2.1、基本结构 2.1.1.构造块 2.1.1.1、事物 2.1.1.2、关系 2.1.1.3、图形 2.1.2.规则 2.1.3.公共机制 2.2、41视图 3、面向对象分析OOA 3.1、用例模型 3.2、分析模型 4、面向对象设计OOD 4.1、细分 4.2、设计原则 5、面向对象的程序设…

一字线模组厂家的选择与使用技巧

在当今自动化与智能制造的浪潮中&#xff0c;一字线模组作为精密定位与传输的核心部件&#xff0c;其性能与质量直接关系到整个生产线的效率与稳定性。因此&#xff0c;选择合适的一字线模组厂家并掌握其使用技巧&#xff0c;对于提升企业竞争力至关重要。接下来我们跟着鑫优威…

四、配置三层交换实验组网

一、实验拓扑 二、实验目的 通过配置交换机&#xff0c;令不同vlan间的主机能够互相通信 三、实验步骤 SW12 <Huawei>undo terminal monitor Info: Current terminal monitor is off. <Huawei>system-view Enter system view, return user view with CtrlZ. [H…