我的编程空间,编程开发者的网络收藏夹
学习永远不晚

SpringBoot+Redis+Lua怎么防止IP重复防刷攻击

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

SpringBoot+Redis+Lua怎么防止IP重复防刷攻击

本篇内容介绍了“SpringBoot+Redis+Lua怎么防止IP重复防刷攻击”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!

黑客或者一些恶意的用户为了攻击你的网站或者APP。通过肉机并发或者死循环请求你的接口。从而导致系统出现宕机。

  • 针对新增数据的接口,会出现大量的重复数据,甚至垃圾数据会将你的数据库和CPU或者内存磁盘耗尽,直到数据库撑爆为止。

  • 针对查询的接口。黑客一般是重点攻击慢查询,比如一个SQL是2S。只要黑客一致攻击,就必然造成系统被拖垮,数据库查询全都被阻塞,连接一直得不到释放造成数据库无法访问。

具体要实现和达到的效果是:
需求:在10秒内,同一IP 127.0.0.1 地址只允许访问30次。
最终达到的效果:

Long execute = this.stringRedisTemplate.execute(defaultRedisScript, keyList, "30", "10");

分析:keylist = 127.0.0.1 expire 30 incr

  • 分析1:用户ip地址127.0.0.1 访问一次 incr

  • 分析2:用户ip地址127.0.0.1 访问一次 incr

  • 分析3:用户ip地址127.0.0.1 访问一次 incr

  • 分析4:用户ip地址127.0.0.1 访问一次 incr

  • 分析10:用户ip地址127.0.0.1 访问一次 incr

  • 判断当前的次数是否以及达到了10次,如果达到了。就时间当前时间是否已经大于30秒。如果没有大于就不允许访问,否则开始设置过期

方法一:根据用户id或者ip来实现

lua文件

在resource/lua下面创建iplimit.lua文件

-- 为某个接口的请求IP设置计数器,比如:127.0.0.1请求课程接口-- KEYS[1] = 127.0.0.1 也就是用户的IP-- ARGV[1] = 过期时间 30m-- ARGV[2] = 限制的次数local limitCount = redis.call('incr',KEYS[1]);if limitCount == 1 then    redis.call("expire",KEYS[1],ARGV[1]) end-- 如果次数还没有过期,并且还在规定的次数内,说明还在请求同一接口if limitCount > tonumber(ARGV[2]) then    return 0endreturn 1

创建lua对象

@SpringBootConfigurationpublic class LuaConfiguration {        @Bean    public DefaultRedisScript<Long> initluascript() {        DefaultRedisScript<Long> defaultRedisScript = new DefaultRedisScript<>();        defaultRedisScript.setScriptSource(new ResourceScriptSource(new ClassPathResource("lua/iplimit.lua")));        defaultRedisScript.setResultType(Long.class);        return defaultRedisScript;    }}

第三步使用

package com.kuangstudy.controller;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.data.redis.core.StringRedisTemplate;import org.springframework.data.redis.core.script.DefaultRedisScript;import org.springframework.web.bind.annotation.PostMapping;import org.springframework.web.bind.annotation.RestController;import java.util.ArrayList;import java.util.List;@RestControllerpublic class IpLuaController {    private static final Logger log = LoggerFactory.getLogger(IpLuaController.class);    @Autowired    private StringRedisTemplate stringRedisTemplate;    @Autowired    private DefaultRedisScript<Long> iplimitLua;    @PostMapping("/ip/limit")    //@IpList(second=10,limit=20)    public String luaupdateuser(String ip) {        String key = "user:" + ip;        // 1: KEYS对应的值,是一个集合        List<String> keysList = new ArrayList<>();        keysList.add(key);        // 2:具体的值ARGV 他是一个动态参数,起也就是一个数组        // 10 代表过期时间 2次数,表述:10秒之内最多允许2次访问        Long execute = stringRedisTemplate.execute(iplimitLua, keysList,"10","2");        if (execute == 0) {            log.info("1----->ip:{},请求收到限制", key);            return "客官,不要太快了服务反应不过来...";        }        log.info("2----->ip:{},正常访问,返回课程列表", key);        return "正常访问,返回课程列表 " + key;    }}

其实还可以自己写一个自定义的注解,结合lua来实现限流
比如:@iplimit(time=10,limit=2)

#方法二:注解实现
需求:用户请求在一秒钟之内只允许2个请求。

核心是AOP

前面几步是一样的,lua脚本,lua对象,自定义redisltemplate。

redis依赖

<dependency>    <groupId>org.springframework.boot</groupId>    <artifactId>spring-boot-starter-aop</artifactId></dependency><dependency>    <groupId>org.springframework.boot</groupId>    <artifactId>spring-boot-starter-web</artifactId></dependency><dependency>    <groupId>org.springframework.boot</groupId>    <artifactId>spring-boot-starter-test</artifactId>    <scope>test</scope></dependency><!--这里就是redis的核心jar包--><dependency>    <groupId>org.springframework.boot</groupId>    <artifactId>spring-boot-starter-data-redis</artifactId></dependency>

lua脚本

-- 为某个接口的请求IP设置计数器,比如:127.0.0.1请求课程接口-- KEYS[1] = 127.0.0.1 也就是用户的IP-- ARGV[1] = 过期时间 30m-- ARGV[2] = 限制的次数local limitCount = redis.call('incr',KEYS[1]);if limitCount == 1 then    redis.call("expire",KEYS[1],ARGV[1])end-- 如果次数还没有过期,并且还在规定的次数内,说明还在请求同一接口if limitCount > tonumber(ARGV[2]) then    return 0endreturn 1

创建lua对象

package com.kuangstudy.config;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.core.io.ClassPathResource;import org.springframework.data.redis.core.script.DefaultRedisScript;import org.springframework.scripting.support.ResourceScriptSource;@Configurationpublic class LuaConfiguration {        @Bean    public DefaultRedisScript<Boolean> limitUserAccessLua() {        // 1: 初始化一个lua脚本的对象DefaultRedisScript        DefaultRedisScript<Boolean> defaultRedisScript = new DefaultRedisScript<>();        // 2: 通过这个对象去加载lua脚本的位置 ClassPathResource读取类路径下的lua脚本        // ClassPathResource 什么是类路径:就是你maven编译好的target/classes目录        defaultRedisScript.setScriptSource(new ResourceScriptSource(new ClassPathResource("lua/userlimit.lua")));        // 3: lua脚本最终的返回值是什么?建议大家都是数字返回。1/0        defaultRedisScript.setResultType(Boolean.class);        return defaultRedisScript;    }}

自定义redistemplate

package com.kuangstudy.config;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.data.redis.connection.RedisConnectionFactory;import org.springframework.data.redis.core.RedisTemplate;import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;import org.springframework.data.redis.serializer.StringRedisSerializer;@Configurationpublic class RedisConfiguration {        @Bean    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {        // 1: 开始创建一个redistemplate        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();        // 2:开始redis连接工厂跪安了        redisTemplate.setConnectionFactory(redisConnectionFactory);        // 创建一个json的序列化方式        GenericJackson2JsonRedisSerializer jackson2JsonRedisSerializer = new GenericJackson2JsonRedisSerializer();        // 设置key用string序列化方式        redisTemplate.setKeySerializer(new StringRedisSerializer());        // 设置value用jackjson进行处理        redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);        // hash也要进行修改        redisTemplate.setHashKeySerializer(new StringRedisSerializer());        redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);        // 默认调用        redisTemplate.afterPropertiesSet();        return redisTemplate;    }}

自定义注解

package com.kuangstudy.limit.annotation;import java.lang.annotation.*;@Target(ElementType.METHOD)@Retention(RetentionPolicy.RUNTIME)@Documentedpublic @interface AccessLimiter {    // 目标: @AccessLimiter(limit="1",timeout="1",key="user:ip:limit")    // 解读:一个用户key在timeout时间内,最多访问limit次    // 缓存的key    String key();    // 限制的次数    int limit() default  1;    // 过期时间    int timeout() default  1;}

自定义切面

package com.kuangstudy.limit.aop;import com.kuangstudy.common.exception.BusinessException;import com.kuangstudy.limit.annotation.AccessLimiter;import org.aspectj.lang.JoinPoint;import org.aspectj.lang.annotation.Aspect;import org.aspectj.lang.annotation.Before;import org.aspectj.lang.annotation.Pointcut;import org.aspectj.lang.reflect.MethodSignature;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.data.redis.core.StringRedisTemplate;import org.springframework.data.redis.core.script.DefaultRedisScript;import org.springframework.stereotype.Component;import org.springframework.util.StringUtils;import java.lang.reflect.Method;import java.util.ArrayList;import java.util.Arrays;import java.util.List;import java.util.stream.Collectors;@Aspect@Componentpublic class AccessLimiterAspect {    private static final Logger log = LoggerFactory.getLogger(AccessLimiterAspect.class);    @Autowired    private StringRedisTemplate stringRedisTemplate;    @Autowired    private DefaultRedisScript<Boolean> limitUserAccessLua;    // 1: 切入点    @Pointcut("@annotation(com.kuangstudy.limit.annotation.AccessLimiter)")    public void cut() {        System.out.println("cut");    }    // 2: 通知和连接点    @Before("cut()")    public void before(JoinPoint joinPoint) {        // 1: 获取到执行的方法        MethodSignature signature = (MethodSignature) joinPoint.getSignature();        Method method = signature.getMethod();        // 2:通过方法获取到注解        AccessLimiter annotation = method.getAnnotation(AccessLimiter.class);        // 如果 annotation==null,说明方法上没加限流AccessLimiter,说明不需要限流操作        if (annotation == null) {            return;        }        // 3: 获取到对应的注解参数        String key = annotation.key();        Integer limit = annotation.limit();        Integer timeout = annotation.timeout();        // 4: 如果你的key是空的        if (StringUtils.isEmpty(key)) {            String name = method.getDeclaringClass().getName();            // 直接把当前的方法名给与key            key = name+"#"+method.getName();            // 获取方法中的参数列表            //ParameterNameDiscoverer pnd = new DefaultParameterNameDiscoverer();            //String[] parameterNames = pnd.getParameterNames(method);            Class<?>[] parameterTypes = method.getParameterTypes();            for (Class<?> parameterType : parameterTypes) {                System.out.println(parameterType);            }            // 如果方法有参数,那么就把key规则 = 方法名“#”参数类型            if (parameterTypes != null) {                String paramtypes = Arrays.stream(parameterTypes)                        .map(Class::getName)                        .collect(Collectors.joining(","));                key = key +"#" + paramtypes;            }        }        // 1: 定义key是的列表        List<String> keysList = new ArrayList<>();        keysList.add(key);        // 2:执行执行lua脚本限流        Boolean accessFlag = stringRedisTemplate.execute(limitUserAccessLua, keysList, limit.toString(), timeout.toString());        // 3: 判断当前执行的结果,如果是0,被限制,1代表正常        if (!accessFlag) {            throw new BusinessException(500, "server is busy!!!");        }    }}

在需要限流的方法上进行限流测试

package com.kuangstudy.controller;import com.kuangstudy.common.exception.BusinessException;import com.kuangstudy.limit.annotation.AccessLimiter;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.data.redis.core.StringRedisTemplate;import org.springframework.data.redis.core.script.DefaultRedisScript;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.RestController;import java.util.ArrayList;import java.util.List;@RestControllerpublic class RateLimiterController {    @Autowired    private StringRedisTemplate stringRedisTemplate;    @Autowired    private DefaultRedisScript<Boolean> limitUserAccessLua;        @GetMapping("/limit/user")    public String limitUser(String userid) {        // 1: 定义key是的列表        List<String> keysList = new ArrayList<>();        keysList.add("user:"+userid);        // 2:执行执行lua脚本限流        Boolean accessFlag = stringRedisTemplate.execute(limitUserAccessLua, keysList, "1","1");        // 3: 判断当前执行的结果,如果是0,被限制,1代表正常        if (!accessFlag) {           throw  new BusinessException(500,"server is busy!!!");        }        return "scucess";    }        @GetMapping("/limit/aop/user")    @AccessLimiter(limit = 1,timeout = 1)    public String limitAopUser(String userid) {        return "scucess";    }    @GetMapping("/limit/aop/user3")    @AccessLimiter(limit = 10,timeout = 1)    public String limitAopUse3(String userid) {        return "scucess";    }        @GetMapping("/limit/aop/user2")    public String limitAopUser2(String userid) {        return "scucess";    }}

“SpringBoot+Redis+Lua怎么防止IP重复防刷攻击”的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识可以关注编程网网站,小编将为大家输出更多高质量的实用文章!

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

SpringBoot+Redis+Lua怎么防止IP重复防刷攻击

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

SpringBoot+Redis+Lua怎么防止IP重复防刷攻击

本篇内容介绍了“SpringBoot+Redis+Lua怎么防止IP重复防刷攻击”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!黑客或者一些恶
2023-06-22

springboot怎么防止接口重复请求

为了防止接口重复请求,可以使用以下方法:1. 幂等性设计:在接口设计时,尽量遵循幂等性的原则,即无论调用多少次,结果应该是相同的。可以使用唯一标识符或者操作序列号来标识请求,并在服务端对重复请求进行判断。2. Token验证:为每个请求生成
2023-10-20

redis怎么防止重复提交操作

为防止重复提交操作,可以使用redis的事务机制和原子操作来实现。以下是一种常见的方法:使用Redis的事务机制和WATCH命令来实现原子性操作。通过WATCH命令监视一个或多个键,如果有其他客户端对这些键进行了修改,那么事务就会失败。在执
redis怎么防止重复提交操作
2024-04-09

Android应用中怎么防止按钮重复点击

本篇文章给大家分享的是有关Android应用中怎么防止按钮重复点击,小编觉得挺实用的,因此分享给大家学习,希望大家阅读完这篇文章后可以有所收获,话不多说,跟着小编一起来看看吧。首先将这块提取为工具类(方便接下来的调用),现在就起名为:But
2023-05-31

SpringBoot+Redis怎么实现防止恶意刷新与暴力请求接口

这篇文章主要介绍“SpringBoot+Redis怎么实现防止恶意刷新与暴力请求接口”,在日常操作中,相信很多人在SpringBoot+Redis怎么实现防止恶意刷新与暴力请求接口问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法
2023-07-02

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录