根据ip限制接口访问次数

2024-05-15 02:28
文章标签 ip 接口 次数 访问 限制

本文主要是介绍根据ip限制接口访问次数,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

前言

我们利用redis去实现这个功能,redis的天然高并发和内存单线程速度拉满,非常适合做这个场景。为了可用性,我们把它封装成注解形式,哪个接口想被根据ip限制接口访问次数,直接标注上注解即可。

一、添加配置

在yaml文件中添加如下配置:

spring.redis.host: 172.xx.xx.xx
spring.redis.port: 6379
spring.redis.database: 1
spring.redis.password: tenxcloud

二、封装注解

封装一个注解使用,并且给一个默认值,防止空指针异常。

package com.xxx.ai.intelligentqa.annotate;import java.lang.annotation.*;/*** 接口访问频率注解,默认一分钟只能访问20次*/@Documented
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RequestLimit {// 限制时间 单位:秒(默认值:一分钟)long period() default 60;// 允许请求的次数(默认值:20次)long count() default 20;
}

三、主体逻辑切面实现

我们利用AOP的切面,来配合注解实现限流逻辑:

package com.xxx.ai.intelligentqa.aop;import com.xxx.ai.intelligentqa.annotate.RequestLimit;
import com.xxx.ai.intelligentqa.tools.RequestUtil;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.BoundSetOperations;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;import javax.servlet.http.HttpServletRequest;
import java.util.concurrent.TimeUnit;@Aspect
@Component
public class RequestLimitAspect {@AutowiredStringRedisTemplate redisTemplate;private static final Logger logger = LoggerFactory.getLogger(RequestLimitAspect.class);private static final String blackListKey = "ai:black_list";// 切点@Pointcut("@annotation(requestLimit)")public void controllerAspect(RequestLimit requestLimit) {}@Around("controllerAspect(requestLimit)")public Object doAround(ProceedingJoinPoint joinPoint, RequestLimit requestLimit) throws Throwable {SseEmitter emitter = new SseEmitter(0L);//获取当前请求request对象ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();if (attributes == null) {throw new IllegalStateException("在上下文中没有请求的属性,可能是非web程序访问。");}HttpServletRequest request = attributes.getRequest();long period = requestLimit.period();long limitCount = requestLimit.count();String ip = RequestUtil.getIpAdrress(request); //解析出来真是请求的ip,防止多重代理ip攻击String uri = request.getRequestURI();String key = "ai:req_limit_".concat(uri).concat(":").concat(ip);// 检查用户IP是否在黑名单中BoundSetOperations<String, String> blackListOperations = redisTemplate.boundSetOps(blackListKey);if (blackListOperations.isMember(ip)) {logger.error("接口拦截:真实IP为{},已经在黑名单中", ip);//这里被我aop环绕的接口返回值是sse类型,所以此处我也需要使用sse形式返回。根据你接口返回值来SseEmitter emitter = new SseEmitter(0L);emitter.send(SseEmitter.event().name(AppConsts.EVENT_ERROR).data("您的请求过于频繁,请于5分钟后再次访问"));emitter.complete();return emitter;}ZSetOperations zSetOperations = redisTemplate.opsForZSet();// 添加当前时间戳long currentMs = System.currentTimeMillis();//zSetOperations.add(key, currentMs, currentMs);zSetOperations.add(key, String.valueOf(currentMs), currentMs);// 设置用户的过期时间redisTemplate.expire(key, period, TimeUnit.SECONDS);// 删除当前窗口之外的值(如果时间窗口是60秒,那么在60秒内的同一IP请求会被计数,超过60秒的请求就不应该再被计数了,因为它们已经滑出时间窗口了)Long aLong = zSetOperations.removeRangeByScore(key, 0, currentMs - period * 1000);// 检查所有可用计数Long count = zSetOperations.zCard(key);if (count > limitCount) {logger.error("接口拦截:{} 请求超过限制频率【{}次/{}s】,真实IP为{}", uri, limitCount, period, ip);// 将用户IP添加到黑名单并设置过期时间为5分钟(300秒)blackListOperations.add(ip);redisTemplate.expire(blackListKey, 300, TimeUnit.SECONDS);SseEmitter emitter = new SseEmitter(0L);emitter.send(SseEmitter.event().name(AppConsts.EVENT_ERROR).data("系统繁忙,请稍后重试"));emitter.complete();return emitter;}// 如果条件不成立,将继续执行 controller 层的方法return  joinPoint.proceed();}
}

四、把注解加在想要限流的接口上

@RequestLimit(count = 30)public Result knowledgeConverseEvents(QAAppDto dto, FacadeBase FacadeBase) {return appIntelligentAS.knowledgeConverseStream(dto);}

这篇关于根据ip限制接口访问次数的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/990547

相关文章

添加7-zip到右键菜单 拒绝访问或者设置无效

想解压缩文件的时候右键发现没有7-zip的选项,然后尝试了两种方法进行设置,都未起效:         方法1、在开始菜单中打开7-zip File Manage->工具-> 选项->7-zip 勾选“添加7-zp到右键菜单”弹出“拒绝访问”弹框,原因是该程序没有通过管理员身份运行,导致写的权限不足。需要右键7-zip File Manage,选择以管理员身份运行,再重复上述操作

ip地址快速切换软件有哪些好处

ip地址快速切换软件有哪些好处?IP地址快速切换软件具有诸多显著的好处,以下是对其主要优势的详细阐述: 首先,IP地址快速切换软件极大地提升了网络活动的灵活性和便捷性。对于需要经常切换网络环境或进行多账号管理的用户而言,这类软件无疑是一个强大的助手。无论是出于工作需要还是个人娱乐需求,用户都可以轻松快速地更改IP地址,从而适应不同的网络环境或满足特定的账号登录要求。 其次,使用IP地址快速

多线程之-监控Lock接口

风吹走了乌云 2017-04-07 20:45 Lock 接口是Java 并发 API提供的最基本的机制来同步代码块。它允许定义临界区。临界区是代码块可以共享资源,但是不能被多个线程同时执行。此机制是通过Lock 接口和 ReentrantLock 类实现的。 在这里,你将学习从Lock对象可以获取的信息和如何获取这些信息。 这里实现的MyLock类扩展了ReentrantLock

AJAX跨域轻松配置nginx允许多个域名跨域访问

极客时间 2017-11-24 19:13:43 什么是跨域 跨域,指的是浏览器不能执行其他网站的脚本。它是由浏览器的同源策略造成的,是浏览器对javascript施加的安全限制。所谓同源是指,域名,协议,端口相同。浏览器执行javascript脚本时,会检查这个脚本属于那个页面,如果不是同源页面,就不会被执行。同源策略的目的,是防止黑客做一些做奸犯科的勾当。比如说,如果一个银行的一个应

【Linux】深入理解文件操作:从C语言接口到系统调用与缓冲区管理

文章目录 前言:1. 铺垫2. 重新使用C文件接口:对比一下重定向2.1. 什么叫当前路径?2.2. 写入文件2.3. 读文件2.4. 程序默认打开的文件流2.5. 输出2.6. 输入 3. 系统调用提供的文件接口3.1. open 打开文件3.2. open函数返回值 4. 缓冲区问题总结: 前言: 在计算机编程中,文件操作是基础且至关重要的技能之一。无论是在系统编程、网络编程

访问限制

自学python如何成为大佬(目录):https://blog.csdn.net/weixin_67859959/article/details/139049996?spm=1001.2014.3001.5501 在类的内部可以定义属性和方法,而在类的外部则可以直接调用属性或方法来操作数据,从而隐藏了类内部的复杂逻辑。但是Python并没有对属性和方法的访问权限进行限制。为了保证类内部的某些属性

Java基础之Comparator比较器接口使用原理

路人宅 2017-04-26 00:22 java.util.Comparator是比较器接口,如果我们需要控制某个类的次序并且该类本身不支持排序,那么就可以建立一个类比较器来进行排序,实现方式很简单只需要实现java.util.Comparator接口。 java.util.Comparator接口只包括两个函数,它的源码如下图: 1) 若一个类要实现java.util.Comp

【PB案例学习笔记】-14使用次数和日期限制

写在前面 这是PB案例学习笔记系列文章的第14篇,该系列文章适合具有一定PB基础的读者。 通过一个个由浅入深的编程实战案例学习,提高编程技巧,以保证小伙伴们能应付公司的各种开发需求。 文章中设计到的源码,小凡都上传到了gitee代码仓库https://gitee.com/xiezhr/pb-project-example.git 需要源代码的小伙伴们可以自行下载查看,后续文章涉及到的案

Java -集合(Set接口)

Set接口 1 特点 ​ 无序且不可重复 2 三种实现类的数据类型与特点 ​ a. HashSet ​ 数据结构:hash表(一维数组) ​ 特点:无序且不可重复 ​ b. LinkedHashSet ​ 数据结构:双向链表 ​ 特点:有序且去重(添加了双向链表的数据结构) ​ ps: ​ 继承关系:class LinkedHashSet extends HashSet ​ c.

vue3-调用API实操-调用开源头像接口

文档部分 这边使用是开源的API 请求地址: :https://api.uomg.com/api/rand.avatar 返回格式 : json/images 请求方式:  get/post 请求实例: https://api.uomg.com/api/rand.avatar?sort=男&format=json 请求参数 请求参数说明 名称必填类型说明sort否string选择输出