欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

SpringBoot+Redis+Lua防止IP重復(fù)防刷攻擊的方法

 更新時間:2021年12月24日 11:57:11   作者:求贊-超人在良家  
本文主要介紹了SpringBoot+Redis+Lua防止IP重復(fù)防刷攻擊的方法,文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下

黑客或者一些惡意的用戶為了攻擊你的網(wǎng)站或者APP。通過肉機并發(fā)或者死循環(huán)請求你的接口。從而導(dǎo)致系統(tǒng)出現(xiàn)宕機。

  • 針對新增數(shù)據(jù)的接口,會出現(xiàn)大量的重復(fù)數(shù)據(jù),甚至垃圾數(shù)據(jù)會將你的數(shù)據(jù)庫和CPU或者內(nèi)存磁盤耗盡,直到數(shù)據(jù)庫撐爆為止。
  • 針對查詢的接口。黑客一般是重點攻擊慢查詢,比如一個SQL是2S。只要黑客一致攻擊,就必然造成系統(tǒng)被拖垮,數(shù)據(jù)庫查詢?nèi)急蛔枞?,連接一直得不到釋放造成數(shù)據(jù)庫無法訪問。

具體要實現(xiàn)和達到的效果是:
需求:在10秒內(nèi),同一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
  • 判斷當(dāng)前的次數(shù)是否以及達到了10次,如果達到了。就時間當(dāng)前時間是否已經(jīng)大于30秒。如果沒有大于就不允許訪問,否則開始設(shè)置過期

方法一:根據(jù)用戶id或者ip來實現(xiàn)

第一步:lua文件

在resource/lua下面創(chuàng)建iplimit.lua文件

-- 為某個接口的請求IP設(shè)置計數(shù)器,比如:127.0.0.1請求課程接口
-- KEYS[1] = 127.0.0.1 也就是用戶的IP
-- ARGV[1] = 過期時間 30m
-- ARGV[2] = 限制的次數(shù)
local limitCount = redis.call('incr',KEYS[1]);
if limitCount == 1 then
    redis.call("expire",KEYS[1],ARGV[1]) 
end
-- 如果次數(shù)還沒有過期,并且還在規(guī)定的次數(shù)內(nèi),說明還在請求同一接口
if limitCount > tonumber(ARGV[2]) then
    return 0
end

return 1

第二步:創(chuàng)建lua對象

@SpringBootConfiguration
public class LuaConfiguration {
    /**
     * 將lua腳本的內(nèi)容加載出來放入到DefaultRedisScript
     * @return
     */
    @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;

/**
 * @description:
 * @author: xuke
 * @time: 2021/7/3 22:25
 */
@RestController
public 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對應(yīng)的值,是一個集合
        List<String> keysList = new ArrayList<>();
        keysList.add(key);
        // 2:具體的值A(chǔ)RGV 他是一個動態(tài)參數(shù),起也就是一個數(shù)組
        // 10 代表過期時間 2次數(shù),表述:10秒之內(nèi)最多允許2次訪問
        Long execute = stringRedisTemplate.execute(iplimitLua, keysList,"10","2");
        if (execute == 0) {
            log.info("1----->ip:{},請求收到限制", key);
            return "客官,不要太快了服務(wù)反應(yīng)不過來...";
        }
        log.info("2----->ip:{},正常訪問,返回課程列表", key);
        return "正常訪問,返回課程列表 " + key;
    }
}

其實還可以自己寫一個自定義的注解,結(jié)合lua來實現(xiàn)限流
比如:@iplimit(time=10,limit=2)

#方法二:注解實現(xiàn)
需求:用戶請求在一秒鐘之內(nèi)只允許2個請求。

核心是AOP

前面幾步是一樣的,lua腳本,lua對象,自定義redisltemplate。

1.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>

2.lua腳本

-- 為某個接口的請求IP設(shè)置計數(shù)器,比如:127.0.0.1請求課程接口
-- KEYS[1] = 127.0.0.1 也就是用戶的IP
-- ARGV[1] = 過期時間 30m
-- ARGV[2] = 限制的次數(shù)
local limitCount = redis.call('incr',KEYS[1]);
if limitCount == 1 then
    redis.call("expire",KEYS[1],ARGV[1])
end
-- 如果次數(shù)還沒有過期,并且還在規(guī)定的次數(shù)內(nèi),說明還在請求同一接口
if limitCount > tonumber(ARGV[2]) then
    return 0
end
return 1

3.創(chuàng)建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;
/**
 * @author 飛哥
 * @Title: 學(xué)相伴出品
 * @Description: 我們有一個學(xué)習(xí)網(wǎng)站:https://www.kuangstudy.com
 * @date 2021/5/21 12:01
 */
@Configuration
public class LuaConfiguration {

    /**
     * 將lua腳本的內(nèi)容加載出來放入到DefaultRedisScript
     * @return
     */
    @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腳本最終的返回值是什么?建議大家都是數(shù)字返回。1/0
        defaultRedisScript.setResultType(Boolean.class);
        return defaultRedisScript;
    }
}

4.自定義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;
/**
 * @author 飛哥
 * @Title: 學(xué)相伴出品
 * @Description: 我們有一個學(xué)習(xí)網(wǎng)站:https://www.kuangstudy.com
 * @date 2021/5/20 13:16
 */
@Configuration
public class RedisConfiguration {

    /**
     * @return org.springframework.data.redis.core.RedisTemplate<java.lang.String, java.lang.Object>
     * @Description 改寫redistemplate序列化規(guī)則
     **/
    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
        // 1: 開始創(chuàng)建一個redistemplate
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
        // 2:開始redis連接工廠跪安了
        redisTemplate.setConnectionFactory(redisConnectionFactory);
        // 創(chuàng)建一個json的序列化方式
        GenericJackson2JsonRedisSerializer jackson2JsonRedisSerializer = new GenericJackson2JsonRedisSerializer();
        // 設(shè)置key用string序列化方式
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        // 設(shè)置value用jackjson進行處理
        redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
        // hash也要進行修改
        redisTemplate.setHashKeySerializer(new StringRedisSerializer());
        redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);
        // 默認調(diào)用
        redisTemplate.afterPropertiesSet();
        return redisTemplate;
    }
}

5.自定義注解

package com.kuangstudy.limit.annotation;

import java.lang.annotation.*;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface AccessLimiter {
    // 目標: @AccessLimiter(limit="1",timeout="1",key="user:ip:limit")
    // 解讀:一個用戶key在timeout時間內(nèi),最多訪問limit次
    // 緩存的key
    String key();
    // 限制的次數(shù)
    int limit() default  1;
    // 過期時間
    int timeout() default  1;
}

6.自定義切面

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
@Component
public 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: 獲取到執(zhí)行的方法
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();

        // 2:通過方法獲取到注解
        AccessLimiter annotation = method.getAnnotation(AccessLimiter.class);
        // 如果 annotation==null,說明方法上沒加限流AccessLimiter,說明不需要限流操作
        if (annotation == null) {
            return;
        }
        // 3: 獲取到對應(yīng)的注解參數(shù)
        String key = annotation.key();
        Integer limit = annotation.limit();
        Integer timeout = annotation.timeout();

        // 4: 如果你的key是空的
        if (StringUtils.isEmpty(key)) {
            String name = method.getDeclaringClass().getName();
            // 直接把當(dāng)前的方法名給與key
            key = name+"#"+method.getName();
            // 獲取方法中的參數(shù)列表

            //ParameterNameDiscoverer pnd = new DefaultParameterNameDiscoverer();
            //String[] parameterNames = pnd.getParameterNames(method);

            Class<?>[] parameterTypes = method.getParameterTypes();
            for (Class<?> parameterType : parameterTypes) {
                System.out.println(parameterType);
            }

            // 如果方法有參數(shù),那么就把key規(guī)則 = 方法名“#”參數(shù)類型
            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:執(zhí)行執(zhí)行l(wèi)ua腳本限流
        Boolean accessFlag = stringRedisTemplate.execute(limitUserAccessLua, keysList, limit.toString(), timeout.toString());
        // 3: 判斷當(dāng)前執(zhí)行的結(jié)果,如果是0,被限制,1代表正常
        if (!accessFlag) {
            throw new BusinessException(500, "server is busy!!!");
        }
    }
}

7.在需要限流的方法上進行限流測試

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;

@RestController
public class RateLimiterController {


    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    @Autowired
    private DefaultRedisScript<Boolean> limitUserAccessLua;


    /**
     * 限流的處理方法
     * @param userid
     * @return
     */
    @GetMapping("/limit/user")
    public String limitUser(String userid) {
        // 1: 定義key是的列表
        List<String> keysList = new ArrayList<>();
        keysList.add("user:"+userid);
        // 2:執(zhí)行執(zhí)行l(wèi)ua腳本限流
        Boolean accessFlag = stringRedisTemplate.execute(limitUserAccessLua, keysList, "1","1");
        // 3: 判斷當(dāng)前執(zhí)行的結(jié)果,如果是0,被限制,1代表正常
        if (!accessFlag) {
           throw  new BusinessException(500,"server is busy!!!");
        }
        return "scucess";
    }


    /**
     * 限流的處理方法
     * @param userid
     * @return
     *
     * 方案1:如果你的一個方法進行限流:一個方法只允許1秒100請求,key公用
     * 方案2:如果你的一個方法進行限流:某個用戶一秒之內(nèi)允許10個請求,key必須要根據(jù)參數(shù)的具體值去執(zhí)行拼接。
     *
     */
    @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";
    }

    /**
     * 限流的處理方法
     * @param userid
     * @return
     *
     * 方案1:如果你的一個方法進行限流:一個方法只允許1秒100請求,key公用
     * 方案2:如果你的一個方法進行限流:某個用戶一秒之內(nèi)允許10個請求,key必須要根據(jù)參數(shù)的具體值去執(zhí)行拼接。
     *
     */
    @GetMapping("/limit/aop/user2")
    public String limitAopUser2(String userid) {
        return "scucess";
    }
}

到此這篇關(guān)于SpringBoot+Redis+Lua防止IP重復(fù)防刷攻擊的方法的文章就介紹到這了,更多相關(guān)SpringBoot防止IP重復(fù)防刷 內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 關(guān)于Java的HashMap多線程并發(fā)問題分析

    關(guān)于Java的HashMap多線程并發(fā)問題分析

    HashMap是采用鏈表解決Hash沖突,因為是鏈表結(jié)構(gòu),那么就很容易形成閉合的鏈路,這樣在循環(huán)的時候只要有線程對這個HashMap進行g(shù)et操作就會產(chǎn)生死循環(huán),本文針對這個問題進行分析,需要的朋友可以參考下
    2023-05-05
  • java正則表達式解析html示例分享

    java正則表達式解析html示例分享

    這篇文章主要介紹了java正則表達式解析html示例,用到獲取url的正則表達式,獲取圖片的正則表達式,需要的朋友可以參考下
    2014-02-02
  • idea推送項目到gitee中的創(chuàng)建方法

    idea推送項目到gitee中的創(chuàng)建方法

    這篇文章主要介紹了idea推送項目到gitee中的創(chuàng)建方法,本文通過圖文實例相結(jié)合給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2021-08-08
  • 5個并發(fā)處理技巧代碼示例

    5個并發(fā)處理技巧代碼示例

    這篇文章主要介紹了5個并發(fā)處理技巧代碼示例,具有一定參考價值,需要的朋友可以了解下。
    2017-10-10
  • 淺談Spring嵌套事務(wù)是怎么回滾的

    淺談Spring嵌套事務(wù)是怎么回滾的

    本文主要介紹了Spring嵌套事務(wù)是怎么回滾的,文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-11-11
  • Java Classloader機制用法代碼解析

    Java Classloader機制用法代碼解析

    這篇文章主要介紹了Java Classloader機制用法代碼解析,涉及JDK默認ClassLoader,雙親委托模型,自定義ClassLoader等相關(guān)內(nèi)容,具有一定借鑒價值,需要的朋友可以參考下
    2018-01-01
  • 如何獲取java新IO的Path文件大小

    如何獲取java新IO的Path文件大小

    這篇文章主要介紹了如何獲取java新IO的Path文件大小,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2019-09-09
  • 詳解Java的JDBC API中事務(wù)的提交和回滾

    詳解Java的JDBC API中事務(wù)的提交和回滾

    這篇文章主要介紹了Java的JDBC API中事務(wù)的提交和回滾,JDBC是Java用于和數(shù)據(jù)庫軟件連接的API,需要的朋友可以參考下
    2015-12-12
  • IntelliJ IDEA語法報錯

    IntelliJ IDEA語法報錯"Usage of API documented as @since 1.6+"的解決

    今天小編就為大家分享一篇關(guān)于IntelliJ IDEA語法報錯"Usage of API documented as @since 1.6+"的解決辦法,小編覺得內(nèi)容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧
    2018-10-10
  • 淺談SpringBoot主流讀取配置文件三種方式

    淺談SpringBoot主流讀取配置文件三種方式

    這篇文章主要介紹了淺談SpringBoot主流讀取配置文件三種方式,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-09-09

最新評論