抵禦惡意http攻擊,使用redis製作一個簡單的防禦

wxt020發表於2020-11-02

一、原理和思路

由於redis是基於記憶體的快取伺服器,效能高,穩定性也經住了市場的考驗,自定義攔截器,連線請求,使用redis來記錄每個http請求ip的近期訪問次數,w如果發現了異常情況(短時間內傳送了大量請求)則可以認為是惡意的HTTP請求攻擊.可以對該IP進行封鎖處理.並且可以記錄該IP到日誌上源:

二、程式步驟

1.引入依賴和配置redis

為了方便測試,我使用工作電腦除錯和執行springboot專案,而redis伺服器搭建在阿里雲上. 

<!--redis 依賴--><dependency>	<groupId>org.springframework.boot</groupId>	<artifactId>spring-boot-starter-data-redis</artifactId></dependency>#redis配置spring.redis.host = ...spring.redis.port = ... [預設6379]spring.redis.password = [如果你的redis伺服器有密碼]

2.自定義redis的序列化器和RedisTemplate

我們需要一種<String, Integer>型別的RedisTemplate,並且要求他的value能夠靈活地在java中作為integer 而在redis中作為string,而不用透過我們手工轉換,同時也能保證在redis伺服器上的可讀性.  springboot上的redis整合api中提供了愚蠢的RedisTemplate<String, Integer> 由於Integer的序列化等問題顯然不符合我們的要求,只能定製 一個自己的RedisTemplate 和序列化器,我習慣建一個config包,在該包的類中透過bean方法建立自己的配置bean

2.1.序列化器

  定義一個類 實現RedisSerializer<Integer>介面,重寫其序列化和反序列化方法

實現思路很簡單,因為我們的目的是在redis中以string字元儲存, 在java中以integer的形式出現,在序列化時候將integer轉換為string再進行序列化,反序列化時將序列化後的string轉換成integer即可

@Overridepublic byte[] serialize(@Nullable Integer integer) throws SerializationException {return integer== null ? null : integer.toString().getBytes(StandardCharsets.UTF_8);}@Overridepublic Integer deserialize(@Nullable byte[] bytes) throws SerializationException {return bytes == null ? null : Integer.valueOf(new String(bytes,StandardCharsets.UTF_8));}

2.2.RedisTemplate

我們可以簡單地建立一個 RedisTemplate<String, Integer> 物件,對其key採用string序列化策略(注意,不要使用愚蠢的jdk預設序列化器,其序列化策略並不會原原本本地將string物件作為字元地byte陣列儲存,而且將其序列化為一種遠古人類使用地奇怪符號) 對其value採用我們上文自定義的序列化器,所以核心的操作是

setKeySerializer(new StringRedisSerializer());

setValueSerializer(intRedisSerializer);

全部程式碼如下

@Configurationpublic class RedisConfig {@AutowiredRedisSerializer<Integer> intRedisSerializer;@Bean("intRedisTemplate")public RedisTemplate<String, Integer> IntRedisTemplate(RedisConnectionFactory rcf){RedisTemplate<String, Integer> re = new RedisTemplate();re.setConnectionFactory(rcf);re.setKeySerializer(new StringRedisSerializer());re.setValueSerializer(intRedisSerializer);return re;}}

說明:我將前面的integer序列化器命名為intRedisSerializer,注入了該 RedisTemplate中

3.偵探類實現檢測http攻擊的核心功能

聰明的你可能已經發現字型的顏色變深了, 這並不是因為我不知道怎麼改字型顏色,而是我們的核心程式碼要開始了!!!

偵探(HttpDetective)這個名字花了我近10分鐘, 該介面只宣告瞭一個inspection(String ip)方法, 決定這個ip是否能訪問你的伺服器, 然後我們再建立HttpDetectiveImpl來具體實現他的功能,我們使用了一種簡單可容錯的策略,忽略了非同步可能導致的實際引數誤差,但是這並不會影響我們的功能和安全性. 具體程式碼如下

@Component("httpDetective")public class HttpDetectiveImpl implements HttpDetective {@Autowiredprivate RedisTemplate<String, Integer> intRedisTemplate;/**單位均為毫秒*/private final int RECORD_TIME = 1000;private final int ALLOW_TIMES = 6;private final int REFUSE_TIME = 180000;@Overridepublic boolean inspection(String ip) {Integer times = intRedisTemplate.opsForValue().get(ip);if(times == null){System.out.println("有正常人進入");intRedisTemplate.opsForValue().set(ip, 1, RECORD_TIME, TimeUnit.MILLISECONDS);return true;}else{if(times >= ALLOW_TIMES){System.out.println("認定為入侵行為 攔截訪問 並且禁止目標短時間內再次訪問並且記錄 入侵者ip"+ ip);intRedisTemplate.opsForValue().set(ip, ALLOW_TIMES, REFUSE_TIME, TimeUnit.MILLISECONDS);return false;}else{System.out.println("有可疑人進入.  "+times);intRedisTemplate.opsForValue().increment(ip);return true;}}}}

我們將前面的 RedisTemplate注入偵探類作為偵探類的工具,為了方便後面的修改,我們定義了三個常量,單位都是毫秒

    RECORD_TIME:  檢查http訪問的時間間隔
    ALLOW_TIMES :  檢測時間間隔內的訪問的次數
    REFUSE_TIME:   對判定為入侵者的封禁時間

我們將客戶端傳入的ip作為redis的鍵,當客戶端首次訪問時候,會建立該鍵值對,並且設定其生存週期 也就是 RECORD_TIME,我們這裡設定為一秒,這一秒內 使用者每當再次訪問介面,對應的value就會自己加一,如果在生存週期內值加到ALLOW_TIMES時,會將其設為入侵者,並且其在REFUSE_TIME時間內無法再訪問我們的url.即對該ip封禁的時間內都會返回false,並且若入侵者繼續嘗試訪問http介面時都會重新整理封禁時間,這裡將其註冊為名字為httpDetective的bean

 

4.攔截器中掛載該偵探類,實現http攔截檢測

4.1 自定義攔截器

由於攔截器透過返回true和false來決定是否發行,這裡只需要直接注入上面的偵探類返回其inspection方法即可.

@AutowiredHttpDetective httpDetective;@Beanpublic HandlerInterceptor visitorRegistration(){return new HandlerInterceptor(){@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {return httpDetective.inspection(request.getRemoteAddr());}};}

4.3 在springboot中註冊攔截器

springboot註冊攔截器非常方便,只要繼承了WebMvcConfigurer並且在其 addInterceptors(InterceptorRegistry registry) 方法中新增改攔截器即可,注意把該類註解為@Configuratio, 這裡不必多說

@Configurationpublic class WebMvcConfig implements WebMvcConfigurer {@ResourceHandlerInterceptor visitorRegistration;@Overridepublic void addInterceptors(InterceptorRegistry registry) {System.out.println("新增攔截器");// TODO Auto-generated method stubregistry.addInterceptor(visitorRegistration)// 攔截路勁.addPathPatterns("/**");}}

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69984164/viewspace-2731701/,如需轉載,請註明出處,否則將追究法律責任。

相關文章