SpringBoot實(shí)現(xiàn)基于URL和IP的訪問(wèn)頻率限制
1. 引言
在現(xiàn)代 Web 應(yīng)用中,接口被惡意刷新或暴力請(qǐng)求是一種常見(jiàn)的攻擊手段。為了保護(hù)系統(tǒng)資源,防止服務(wù)器過(guò)載或服務(wù)不可用,需要對(duì)接口的訪問(wèn)頻率進(jìn)行限制。本文將介紹如何使用 Spring Boot 實(shí)現(xiàn)基于 URL 和 IP 的訪問(wèn)頻率限制,具體步驟包括:
使用攔截器攔截請(qǐng)求:在每個(gè)請(qǐng)求到達(dá)控制器之前進(jìn)行攔截。
使用 Redis 存儲(chǔ)訪問(wèn)記錄:利用 Redis 的高性能特性來(lái)存儲(chǔ)每個(gè) IP 對(duì)每個(gè) URL 的訪問(wèn)次數(shù)。
檢測(cè)訪問(wèn)頻率:判斷 IP 在一定時(shí)間內(nèi)對(duì)特定 URL 的訪問(wèn)次數(shù)是否超過(guò)限制。
禁用惡意 IP:如果超過(guò)限制,則將 IP 列入黑名單,禁止其后續(xù)訪問(wèn)。
2. 項(xiàng)目依賴
首先,在 pom.xml 中添加必要的依賴,包括 Spring Boot Web、Spring Boot Starter Data Redis 和 Lombok(用于簡(jiǎn)化代碼)。
<!-- Spring Boot Starter Data Redis --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> <version>3.4.1</version> </dependency> <!-- Lombok (可選,用于簡(jiǎn)化代碼) --> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency>
3. 配置 Redis
在 application.properties 中配置 Redis 連接信息:
server.port=8080 # Redis 配置 spring.redis.host=127.0.0.1 spring.redis.port=6379 spring.redis.database=0 # 可選:設(shè)置密碼 # spring.redis.password=yourpassword
或在 application.yml 中配置Redis連接信息:
server: port: 8080 spring: application: name: urlInterceptorDemo data: # redis 配置 redis: # 地址 host: 127.0.0.1 # 端口,默認(rèn)為6379 port: 6379 # 數(shù)據(jù)庫(kù)索引 database: 0 # 密碼 password: "123456" # 連接超時(shí)時(shí)間 timeout: 10s
4. 創(chuàng)建攔截器
創(chuàng)建一個(gè)攔截器 RateLimitInterceptor,用于攔截每個(gè)請(qǐng)求并執(zhí)行訪問(wèn)頻率限制邏輯。
package com.yyqq.urlinterceptordemo.Interceptor; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Component; import org.springframework.web.servlet.HandlerInterceptor; import java.util.concurrent.TimeUnit; @Component public class RateLimitInterceptor implements HandlerInterceptor { @Autowired public RedisTemplate redisTemplate; // 訪問(wèn)頻率限制:每個(gè) IP 每個(gè) URL 最多訪問(wèn) 100 次 / 分鐘 private static final int MAX_REQUESTS = 10; private static final int TIME_WINDOW = 60; // 秒 @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { String ip = getClientIP(request); String url = request.getRequestURI(); String key = "rate_limit:" + url + ":" + ip; long count = redisTemplate.opsForValue().increment(key, 1); if (count == 1) { // 設(shè)置鍵的過(guò)期時(shí)間為 TIME_WINDOW 秒 redisTemplate.expire(key, TIME_WINDOW, TimeUnit.SECONDS); } if (count > MAX_REQUESTS) { response.setStatus(HttpServletResponse.SC_FORBIDDEN); response.setContentType("text/html;charset=UTF-8"); response.getWriter().write("請(qǐng)求過(guò)于頻繁,請(qǐng)稍后再試。"); return false; } return true; } private String getClientIP(HttpServletRequest request) { String ip = request.getHeader("X-Forwarded-For"); if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) { ip = request.getHeader("Proxy-Client-IP"); } if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) { ip = request.getHeader("WL-Proxy-Client-IP"); } if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) { ip = request.getRemoteAddr(); } return ip; } }
代碼說(shuō)明
RedisTemplate:用于與 Redis 進(jìn)行交互。
MAX_REQUESTS 和 TIME_WINDOW:定義每個(gè) IP 在每個(gè) URL 上的最大訪問(wèn)次數(shù)和時(shí)間窗口(60 秒)。
preHandle 方法:
獲取客戶端 IP 和請(qǐng)求的 URL。
構(gòu)建 Redis 鍵,例如 rate_limit:/api/data:192.168.1.1。
使用 increment 方法對(duì)鍵進(jìn)行遞增,并設(shè)置過(guò)期時(shí)間。
如果訪問(wèn)次數(shù)超過(guò) MAX_REQUESTS,則返回 403 狀態(tài)碼,并返回錯(cuò)誤信息。
getClientIP 方法:獲取客戶端的真實(shí) IP,處理代理和負(fù)載均衡的情況。
5. 注冊(cè)攔截器
創(chuàng)建一個(gè)配置類 WebConfig,將攔截器注冊(cè)到 Spring MVC 中。
package com.yyqq.urlinterceptordemo.config; import com.yyqq.urlinterceptordemo.Interceptor.RateLimitInterceptor; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.*; @Configuration public class WebConfig implements WebMvcConfigurer { @Autowired private RateLimitInterceptor rateLimitInterceptor; @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(rateLimitInterceptor) .addPathPatterns("/**") // 攔截所有路徑 .excludePathPatterns("/error"); // 排除錯(cuò)誤路徑 } }
代碼說(shuō)明
- addInterceptors 方法:將自定義的攔截器添加到攔截器鏈中,并指定攔截的路徑模式。
- addPathPatterns("/")**:攔截所有路徑。
- excludePathPatterns(“/error”):排除錯(cuò)誤路徑,避免攔截器影響錯(cuò)誤處理。
6. 創(chuàng)建控制器
創(chuàng)建一個(gè)簡(jiǎn)單的控制器 DemoController,包含一個(gè)示例接口用于測(cè)試。
package com.yyqq.urlinterceptordemo.controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/test") public class DemoController { @GetMapping("/getData") public String getData() { return "這是數(shù)據(jù)接口,現(xiàn)在訪問(wèn)正常!"; } }
8. 測(cè)試
啟動(dòng) Spring Boot 應(yīng)用后,可以進(jìn)行以下測(cè)試:
1.正常訪問(wèn):
訪問(wèn) http://localhost:8080/test/getData,應(yīng)返回 “這是數(shù)據(jù)接口,現(xiàn)在訪問(wèn)正常!”。
多次快速刷新,訪問(wèn)次數(shù)達(dá)到 10 次后,應(yīng)返回 “請(qǐng)求過(guò)于頻繁,請(qǐng)稍后再試。”,并返回 403 狀態(tài)碼。
2.禁用 IP:
在達(dá)到限制后,等待一段時(shí)間(60 秒),再次訪問(wèn)應(yīng)恢復(fù)正常。
9. 總結(jié)
通過(guò)結(jié)合使用 Spring Boot 攔截器和 Redis,本文實(shí)現(xiàn)了一種基于 URL 和 IP 的訪問(wèn)頻率限制機(jī)制。這種機(jī)制能夠有效地防止接口被惡意刷新和暴力請(qǐng)求,保護(hù)系統(tǒng)資源,提高應(yīng)用的安全性和穩(wěn)定性。在實(shí)際應(yīng)用中,可以根據(jù)具體需求調(diào)整訪問(wèn)頻率限制的參數(shù),如最大訪問(wèn)次數(shù)和時(shí)間窗口。此外,還可以結(jié)合其他安全措施,如 IP 黑名單、驗(yàn)證碼等,進(jìn)一步增強(qiáng)系統(tǒng)的防護(hù)能力。
到此這篇關(guān)于SpringBoot實(shí)現(xiàn)基于URL和IP的訪問(wèn)頻率限制的文章就介紹到這了,更多相關(guān)SpringBoot訪問(wèn)頻率限制內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
詳解Springboot+React項(xiàng)目跨域訪問(wèn)問(wèn)題
這篇文章主要介紹了詳解Springboot+React項(xiàng)目跨域訪問(wèn)問(wèn)題,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-11-11Future與FutureTask接口實(shí)現(xiàn)示例詳解
這篇文章主要為大家介紹了Future與FutureTask接口實(shí)現(xiàn)示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-10-10Apache?Hudi異步Clustering部署操作的掌握
這篇文章主要介紹了Apache?Hudi異步Clustering部署操作的掌握,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步早日升職加薪2022-03-03Json字符串轉(zhuǎn)Java對(duì)象和List代碼實(shí)例
這篇文章主要介紹了Json字符串轉(zhuǎn)Java對(duì)象和List代碼實(shí)例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-06-06Java?Spring的核心與設(shè)計(jì)思想你知道嗎
這篇文章主要為大家詳細(xì)介紹了Java?Spring的核心與設(shè)計(jì)思想,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來(lái)幫助2022-03-03SpringBoot整合Mybatis-plus關(guān)鍵詞模糊查詢結(jié)果為空
SpringBoot整合Mybatis-plus使用關(guān)鍵詞模糊查詢的時(shí)候,數(shù)據(jù)庫(kù)中有數(shù)據(jù),但是無(wú)法查找出來(lái),本文就來(lái)介紹一下SpringBoot整合Mybatis-plus關(guān)鍵詞模糊查詢結(jié)果為空的解決方法2025-04-04在IntelliJ IDEA中.idea文件是什么可以刪除嗎
相信有很多小伙伴,在用idea寫(xiě)java代碼的時(shí)候,創(chuàng)建工程總是會(huì)出現(xiàn).idea文件,該文件也從來(lái)沒(méi)去打開(kāi)使用過(guò),那么它在我們項(xiàng)目里面,扮演什么角色,到底能不能刪除它呢?這篇文章主要介紹了在IntelliJ IDEA中.idea文件是什么可以刪除嗎,需要的朋友可以參考下2024-01-01spring AOP實(shí)現(xiàn)@Around輸出請(qǐng)求參數(shù)和返回參數(shù)
這篇文章主要介紹了spring AOP實(shí)現(xiàn)@Around輸出請(qǐng)求參數(shù)和返回參數(shù),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-02-02