SpringBoot實(shí)現(xiàn)基于URL和IP的訪問頻率限制
1. 引言
在現(xiàn)代 Web 應(yīng)用中,接口被惡意刷新或暴力請(qǐng)求是一種常見的攻擊手段。為了保護(hù)系統(tǒng)資源,防止服務(wù)器過載或服務(wù)不可用,需要對(duì)接口的訪問頻率進(jìn)行限制。本文將介紹如何使用 Spring Boot 實(shí)現(xiàn)基于 URL 和 IP 的訪問頻率限制,具體步驟包括:
使用攔截器攔截請(qǐng)求:在每個(gè)請(qǐng)求到達(dá)控制器之前進(jìn)行攔截。
使用 Redis 存儲(chǔ)訪問記錄:利用 Redis 的高性能特性來存儲(chǔ)每個(gè) IP 對(duì)每個(gè) URL 的訪問次數(shù)。
檢測訪問頻率:判斷 IP 在一定時(shí)間內(nèi)對(duì)特定 URL 的訪問次數(shù)是否超過限制。
禁用惡意 IP:如果超過限制,則將 IP 列入黑名單,禁止其后續(xù)訪問。
2. 項(xiàng)目依賴
首先,在 pom.xml 中添加必要的依賴,包括 Spring Boot Web、Spring Boot Starter Data Redis 和 Lombok(用于簡化代碼)。
<!-- 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 (可選,用于簡化代碼) -->
<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ù)庫索引
database: 0
# 密碼
password: "123456"
# 連接超時(shí)時(shí)間
timeout: 10s
4. 創(chuàng)建攔截器
創(chuàng)建一個(gè)攔截器 RateLimitInterceptor,用于攔截每個(gè)請(qǐng)求并執(zhí)行訪問頻率限制邏輯。
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;
// 訪問頻率限制:每個(gè) IP 每個(gè) URL 最多訪問 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è)置鍵的過期時(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)求過于頻繁,請(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;
}
}代碼說明
RedisTemplate:用于與 Redis 進(jìn)行交互。
MAX_REQUESTS 和 TIME_WINDOW:定義每個(gè) IP 在每個(gè) URL 上的最大訪問次數(shù)和時(shí)間窗口(60 秒)。
preHandle 方法:
獲取客戶端 IP 和請(qǐng)求的 URL。
構(gòu)建 Redis 鍵,例如 rate_limit:/api/data:192.168.1.1。
使用 increment 方法對(duì)鍵進(jìn)行遞增,并設(shè)置過期時(shí)間。
如果訪問次數(shù)超過 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ò)誤路徑
}
}
代碼說明
- addInterceptors 方法:將自定義的攔截器添加到攔截器鏈中,并指定攔截的路徑模式。
- addPathPatterns("/")**:攔截所有路徑。
- excludePathPatterns(“/error”):排除錯(cuò)誤路徑,避免攔截器影響錯(cuò)誤處理。
6. 創(chuàng)建控制器
創(chuàng)建一個(gè)簡單的控制器 DemoController,包含一個(gè)示例接口用于測試。
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)在訪問正常!";
}
}
8. 測試
啟動(dòng) Spring Boot 應(yīng)用后,可以進(jìn)行以下測試:
1.正常訪問:
訪問 http://localhost:8080/test/getData,應(yīng)返回 “這是數(shù)據(jù)接口,現(xiàn)在訪問正常!”。
多次快速刷新,訪問次數(shù)達(dá)到 10 次后,應(yīng)返回 “請(qǐng)求過于頻繁,請(qǐng)稍后再試。”,并返回 403 狀態(tài)碼。

2.禁用 IP:
在達(dá)到限制后,等待一段時(shí)間(60 秒),再次訪問應(yīng)恢復(fù)正常。

9. 總結(jié)
通過結(jié)合使用 Spring Boot 攔截器和 Redis,本文實(shí)現(xiàn)了一種基于 URL 和 IP 的訪問頻率限制機(jī)制。這種機(jī)制能夠有效地防止接口被惡意刷新和暴力請(qǐng)求,保護(hù)系統(tǒng)資源,提高應(yīng)用的安全性和穩(wěn)定性。在實(shí)際應(yīng)用中,可以根據(jù)具體需求調(diào)整訪問頻率限制的參數(shù),如最大訪問次數(shù)和時(shí)間窗口。此外,還可以結(jié)合其他安全措施,如 IP 黑名單、驗(yàn)證碼等,進(jìn)一步增強(qiáng)系統(tǒng)的防護(hù)能力。
到此這篇關(guān)于SpringBoot實(shí)現(xiàn)基于URL和IP的訪問頻率限制的文章就介紹到這了,更多相關(guān)SpringBoot訪問頻率限制內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
詳解Springboot+React項(xiàng)目跨域訪問問題
這篇文章主要介紹了詳解Springboot+React項(xiàng)目跨域訪問問題,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-11-11
Future與FutureTask接口實(shí)現(xiàn)示例詳解
這篇文章主要為大家介紹了Future與FutureTask接口實(shí)現(xiàn)示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-10-10
Apache?Hudi異步Clustering部署操作的掌握
這篇文章主要介紹了Apache?Hudi異步Clustering部署操作的掌握,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步早日升職加薪2022-03-03
Json字符串轉(zhuǎn)Java對(duì)象和List代碼實(shí)例
這篇文章主要介紹了Json字符串轉(zhuǎn)Java對(duì)象和List代碼實(shí)例,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-06-06
Java?Spring的核心與設(shè)計(jì)思想你知道嗎
這篇文章主要為大家詳細(xì)介紹了Java?Spring的核心與設(shè)計(jì)思想,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助2022-03-03
SpringBoot整合Mybatis-plus關(guān)鍵詞模糊查詢結(jié)果為空
SpringBoot整合Mybatis-plus使用關(guān)鍵詞模糊查詢的時(shí)候,數(shù)據(jù)庫中有數(shù)據(jù),但是無法查找出來,本文就來介紹一下SpringBoot整合Mybatis-plus關(guān)鍵詞模糊查詢結(jié)果為空的解決方法2025-04-04
在IntelliJ IDEA中.idea文件是什么可以刪除嗎
相信有很多小伙伴,在用idea寫java代碼的時(shí)候,創(chuàng)建工程總是會(huì)出現(xiàn).idea文件,該文件也從來沒去打開使用過,那么它在我們項(xiàng)目里面,扮演什么角色,到底能不能刪除它呢?這篇文章主要介紹了在IntelliJ IDEA中.idea文件是什么可以刪除嗎,需要的朋友可以參考下2024-01-01
spring 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

