使用Spring Boot自定义注解 + AOP实现基于IP的接口限流和黑白名单

news2025/1/23 3:15:51

在这里插入图片描述

😄 19年之后由于某些原因断更了三年,23年重新扬帆起航,推出更多优质博文,希望大家多多支持~
🌷 古之立大事者,不惟有超世之才,亦必有坚忍不拔之志
🎐 个人CSND主页——Micro麦可乐的博客
🐥《Docker实操教程》专栏以最新的Centos版本为基础进行Docker实操教程,入门到实战
🌺《RabbitMQ》本专栏主要介绍使用JAVA开发RabbitMQ的系列教程,从基础知识到项目实战
🌸《设计模式》专栏以实际的生活场景为案例进行讲解,让大家对设计模式有一个更清晰的理解
💕《Jenkins实战》专栏主要介绍Jenkins+Docker+Git+Maven的实战教程,让你快速掌握项目CI/CD,是2024年最新的实战教程
如果文章能够给大家带来一定的帮助!欢迎关注、评论互动~

使用 Spring Boot 自定义注解和AOP实现基于IP的接口限流和黑白名单

  • 前言
  • 项目初始化
  • 自定义限流注解
  • 编写限流切面
  • Controller中使用限流注解
  • 接口测试
  • 结语

前言

在我们日常开发的项目中为了保证系统的稳定性,很多时候我们需要对系统做限流处理,它可以有效防止恶意请求对系统造成过载。常见的限流方案主要有:

  • 网关限流NGINXZuul 等 API 网关
  • 服务器端限流:服务端接口限流
  • 令牌桶算法:通过定期生成令牌放入桶中,请求需要消耗令牌才能通过
  • 熔断机制HystrixResilience4j

本文将详细介绍 Spring Boot 通过自定义注解和 AOP(面向切面编程),实现基于 IP 的限流和黑白名单功能,包括如何使用 Redis 存储限流和黑名单信息。

项目初始化

首先,创建一个 Spring Boot 项目,并添加必要的依赖。在 pom.xml 文件中添加以下内容:

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-aop</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>
</dependencies>

配置 application.yml 加入 redis 配置

spring:
    #redis
    redis:
        # 地址
        host: 127.0.0.1
        # 端口,默认为6379
        port: 6379

自定义限流注解

创建一个自定义注解 RateLimit

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface RateLimit {
	//限制次数
    int limit() default 5;
    //限制时间 秒
    int timeout() default 60;
}

编写限流切面

使用 AOP 实现限流逻辑,并增加 IP 黑白名单判断 , 使用 Redis 来存储和检查请求次数及黑名单信息。

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import java.util.concurrent.TimeUnit;

@Aspect
@Component
public class RateLimitAspect {

    @Autowired
    private StringRedisTemplate redisTemplate;

    @Autowired
    private HttpServletRequest request;
	//定义黑名单key前缀
    private static final String BLACKLIST_KEY_PREFIX = "blacklist:";
    //定义白名单key前缀
    private static final String WHITELIST_KEY_PREFIX = "whitelist:";

    @Around("@annotation(rateLimit)")
    public Object rateLimit(ProceedingJoinPoint joinPoint, RateLimit rateLimit) throws Throwable {
    	//获取IP
        String ip = request.getRemoteAddr();
        
        //黑名单则直接异常
        if (isBlacklisted(ip)) {
            throw new RuntimeException("超出访问限制已加入黑名单,1小时后再访问");
        }

		//如果是白名单下的不做限制
        if (isWhitelisted(ip)) {
            return joinPoint.proceed();
        }

        String key = generateKey(joinPoint, ip);
        int limit = rateLimit.limit();
        int timeout = rateLimit.timeout();
        
        String countStr = redisTemplate.opsForValue().get(key);
        int count = countStr == null ? 0 : Integer.parseInt(countStr);

        if (count < limit) {
            redisTemplate.opsForValue().set(key, String.valueOf(count + 1), timeout, TimeUnit.SECONDS);
            return joinPoint.proceed();
        } else {
            addToBlacklist(ip);
            throw new RuntimeException("超出请求限制IP已被列入黑名单");
        }
    }

    private boolean isBlacklisted(String ip) {
        return redisTemplate.hasKey(BLACKLIST_KEY_PREFIX + ip);
    }

    private boolean isWhitelisted(String ip) {
        return redisTemplate.hasKey(WHITELIST_KEY_PREFIX + ip);
    }

    private void addToBlacklist(String ip) {
        redisTemplate.opsForValue().set(BLACKLIST_KEY_PREFIX + ip, "true", 1, TimeUnit.HOURS);
    }

    private String generateKey(ProceedingJoinPoint joinPoint, String ip) {
        String methodName = joinPoint.getSignature().getName();
        String className = joinPoint.getTarget().getClass().getName();
        return className + ":" + methodName + ":" + ip;
    }
}

Controller中使用限流注解

创建一个简单的限流测试Controller,并在需要限流的方法上使用 @RateLimit 注解:,需要编写异常处理,返回RateLimitAspect异常信息,并以字符串形式返回

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/api")
public class TestController {

	//由于是简单的测试项目,这里就直接定义异常处理,并为采用全局异常处理
	@ExceptionHandler(value = Exception.class)
    public String handleException(Exception ex) {
        return ex.getMessage();
    }

    @RateLimit(limit = 5, timeout = 60)
    @GetMapping("/limit")
    public String testRateLimit() {
        return "Request successful!";
    }
}

接口测试

使用接口调试工具,请求接口测试,博主这里使用的是 Apifox,我们60秒内请求5次
前5次均返回 Request successful!
第6次会提示 超出请求限制IP已被列入黑名单

在这里插入图片描述

结语

通过以上步骤,我们成功地使用自定义注解和 AOP 实现了基于 IP 的接口限流和黑白名单功能。这种方法不仅简化了限流逻辑的实现,还增强了系统的安全性和稳定性。小伙伴们可以根据实际需求对限流逻辑进行进一步扩展和优化,例如增加不同的限流策略、动态调整限流参数等。


在这里插入图片描述

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

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

相关文章

德人合科技——天锐绿盾内网安全管理软件 | -文档透明加密模块

天锐绿盾文档加密功能能够为各种模式的电子文档提供高强度加密保护&#xff0c;丰富的权限控制以及灵活的应用管理&#xff0c;帮助企业构建更严密的立体保密体系。 PC地址&#xff1a; https://isite.baidu.com/site/wjz012xr/2eae091d-1b97-4276-90bc-6757c5dfedee ————…

VSCODE 常用快捷键

快捷按键 注释 CTRL /CTRL KSHIFT ALT A取消注释 CTRL /CTRL KSHIFT ALT A搜索文件 Ctrl P移动到某一行 Ctrl g打开一个新窗口 Ctrl Shift N关闭窗口 Ctrl Shift W新建文件 Ctrl N文件间切换 Ctrl Tab全部文件搜索 Ctrl Shift F全屏 F11 打开文件出现中文乱码 文件右下角…

极验4点选逆向 JS逆向分析 最新版验证码

目录 声明&#xff01; 一、请求流程分析 二、加密参数w与payload 三、参数w生成位置 四、结果展示&#xff1a; 原创文章&#xff0c;请勿转载&#xff01; 本文内容仅限于安全研究&#xff0c;不公开具体源码。维护网络安全&#xff0c;人人有责。 声明&#xff01; 本文章…

268 基于matlab的模拟双滑块连杆机构运动

基于matlab的模拟双滑块连杆机构运动&#xff0c;并绘制运动动画&#xff0c;连杆轨迹可视化输出&#xff0c;并输出杆件质心轨迹、角速度、速度变化曲线。可定义杆长、滑块速度&#xff0c;滑块初始位置等参数。程序已调通&#xff0c;可直接运行。 268 双滑块连杆机构运动 连…

Dinky DorisCDC 整库同步到 Doris

doris flinkcdc语法参考 Flink Doris Connector - Apache Doris 参考&#xff1a; Doris Flink DolphinScheduler Dinky 构建开源数据平台_dinky dolphinscheduler flink-CSDN博客

【SpringMVC】_SpringMVC实现用户登录

目录 1、需求分析 2、接口定义 2.1 校验接口 请求参数 响应数据 2.2 查询登录用户接口 请求参数 响应数据 4、服务器代码 5、前端代码 5.1 登录页面login.html 5.2 首页页面index.html 6、运行测试 1、需求分析 用户输入账号与密码&#xff0c;后端校验密码是否正确&a…

使用opencv 进行车牌位置检测的源代码

效果: 这一个车牌识别系统中的预处理函数,其主要目的是对输入的车牌图片进行一系列的图像处理操作,以便后续的车牌识别算法能够更准确地识别出车牌。 整个函数的流程是:读取图像 -> 缩放 -> 灰度化 -> 去噪 -> 边缘检测 -> 形态学操作 -> 轮廓检测 ->…

科普健康短视频:成都鼎茂宏升文化传媒公司

科普健康短视频&#xff1a;引领健康知识新潮流 在数字化时代的浪潮中&#xff0c;短视频以其短小精悍、直观易懂的特点&#xff0c;迅速成为大众获取信息的重要渠道。其中&#xff0c;科普健康短视频更是凭借其科学、权威、实用的内容&#xff0c;吸引了大量关注健康的观众。…

【C++ ——— 继承】

文章目录 继承的概念即定义继承概念继承定义定义格式继承关系和访问限定符继承基类成员访问方式的变化 基类对象和派生类对象的赋值转换继承中的作用域派生类中的默认成员函数继承与友元继承与静态成员菱形继承虚继承解决数据冗余和二义性的原理继承的总结继承常见笔试面试题 继…

Unity中模拟生成正态分布的一种方式

using System; using System.Collections; using System.Collections.Generic; using Unity.Mathematics; using UnityEngine;public class MathFunction : MonoBehaviour {private void Start(){//key 范围 0-99 表示 0% 到 99%Dictionary<int,uint> m new Dictionary&…

Jmeter性能测试-【关联,提取器】

新知识点 关联&#xff1a; 正则表达式提取器 边界提取器 XPath提取器 JSON提取器 梳理框架 1. Jmeter基础 定义&#xff1a;Jmeter是一个开源的性能测试工具&#xff0c;主要用于Web应用和各种服务的性能测试。 主要功能&#xff1a;可以模拟多用户并发访问&#xff0c;测…

【Python】解决Python报错:AttributeError: ‘str‘ object has no attribute ‘xxx‘

&#x1f9d1; 博主简介&#xff1a;阿里巴巴嵌入式技术专家&#xff0c;深耕嵌入式人工智能领域&#xff0c;具备多年的嵌入式硬件产品研发管理经验。 &#x1f4d2; 博客介绍&#xff1a;分享嵌入式开发领域的相关知识、经验、思考和感悟&#xff0c;欢迎关注。提供嵌入式方向…

计算机网络-BGP状态机制与对等体表项

前面我们讲了BGP交互后需要建立对等体&#xff0c;BGP存在两种对等体关系类型&#xff1a;EBGP及IBGP&#xff0c;那对等体建立过程的状态是怎样的呢&#xff1f;BGP报文我们也学习过了&#xff0c;现在通过结合起来了解下BGP的状态机以及对等体表。 一、BGP状态机 也就是两台路…

数据库设计:实体关系图

一个良好的设计对于数据库系统至关重要&#xff0c;它可以减少数据冗余&#xff0c;确保数据的一致性和完整性&#xff0c;同时使得数据库易于维护和扩展。 实体关系图&#xff08;Entity-Relationship Diagram、ERD&#xff09;是一种用于数据库设计的结构图&#xff0c;它描…

【再探】设计模式—中介者模式、观察者模式及模板方法模式

中介者模式让多对多的复杂引用关系变成一对多&#xff0c;同时能通过中间类来封装多个类中的行为&#xff0c;观察者模式在目标状态更新时能自动通知给订阅者&#xff0c;模版方法模式则是控制方法的执行顺序&#xff0c;子类在不改变算法的结构基础上可以扩展功能实现。 1 中…

ESP8266使用AT指令登陆新版OneNET平台进行固定数据上报

登陆OneNET进开发者中心 创建产品 创建云平台产品 产品类别和智能化方式选择 产品名称和城市自定义选择&#xff0c;框选部分参照下图&#xff0c;开发方案选标准方案时平台会预置标准物模型和App控制面板&#xff0c;选自定义方案用户可自行定义物模型和App控制面板&…

RedisTemplate实战应用--队列等

一、RedisTemplate队列插入 1、从集合左边插入值 https://blog.csdn.net/weixin_43658899/article/details/121040307 leftPush(K key, V value) redisTemplate.opsForList().leftPush("leftdatakey","bbbb");2、从集合左边开始在v1值后边插入新值v2 le…

忘记论文Word文档密码,如何找回加密密码

论文的撰写与保存是每位学者都需面对的重要任务。为了保护自己的劳动成果&#xff0c;许多学者会选择对Word文档进行加密&#xff0c;设置密码以确保文档的安全。然而&#xff0c;有时我们可能会因为各种原因忘记了文档密码&#xff0c;这无疑会给我们的工作带来诸多不便。那么…

跨境电商如何有效做好店铺账号管理?

跨境电商有效做好店铺账号管理至关重要&#xff0c;类似亚马逊、Temu、TikTok、ebay跨境电商账号涉及多个方面&#xff0c;包括多个账户的安全性、合规性、操作效率等。以下是一些我自己实操的策略和实践&#xff0c;希望能够帮助大家更好地管理跨境电商店铺账号。 一、哪些行为…

sqlite--SQL语句进阶

SQL语句进阶 函数和聚合 函数&#xff1a; SQL 语句支持利用函数来处理数据&#xff0c; 函数一般是在数据上执行的&#xff0c; 它给数据的转换和处理提供了方便常用的文本处理函数&#xff1a; 常用的文本处理函数&#xff1a; // 返回字符串的长度 length();//将字符串…