一種基于注解與AOP的Spring Boot接口限流防刷方案

1. 添加Maven依賴

<dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency>
</dependencies>

2. 創建自定義注解

import java.lang.annotation.*;/*** 接口防刷注解*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Documented
public @interface AccessLimit {/*** 限制時間范圍(秒)*/int time() default 60;/*** 時間范圍內最大訪問次數*/int maxCount() default 10;/*** 是否檢查IP地址*/boolean checkIp() default true;/*** 是否檢查用戶身份(需要登錄)*/boolean checkUser() default false;/*** 觸發限制時的提示信息*/String message() default "操作過于頻繁,請稍后再試";
}

3. 創建AOP切面實現防護邏輯

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.concurrent.TimeUnit;@Aspect
@Component
public class AccessLimitAspect {@Autowiredprivate RedisTemplate<String, Object> redisTemplate;@Around("@annotation(accessLimit)")public Object around(ProceedingJoinPoint joinPoint, AccessLimit accessLimit) throws Throwable {// 獲取請求對象ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();if (attributes == null) {return joinPoint.proceed();}HttpServletRequest request = attributes.getRequest();MethodSignature signature = (MethodSignature) joinPoint.getSignature();Method method = signature.getMethod();// 構建Redis keyString key = buildKey(request, method, accessLimit);// 獲取當前計數ValueOperations<String, Object> operations = redisTemplate.opsForValue();Integer count = (Integer) operations.get(key);if (count == null) {// 第一次訪問operations.set(key, 1, accessLimit.time(), TimeUnit.SECONDS);} else if (count < accessLimit.maxCount()) {// 計數增加operations.increment(key);} else {// 超出限制,拋出異常throw new RuntimeException(accessLimit.message());}return joinPoint.proceed();}/*** 構建Redis key*/private String buildKey(HttpServletRequest request, Method method, AccessLimit accessLimit) {StringBuilder key = new StringBuilder("access_limit:");// 添加方法標識key.append(method.getDeclaringClass().getName()).append(".").append(method.getName()).append(":");// 添加IP標識if (accessLimit.checkIp()) {String ip = getClientIp(request);key.append(ip).append(":");}// 添加用戶標識(需要實現獲取當前用戶的方法)if (accessLimit.checkUser()) {// 這里需要根據你的用戶系統實現獲取當前用戶ID的方法String userId = getCurrentUserId();if (userId != null) {key.append(userId).append(":");}}return key.toString();}/*** 獲取客戶端IP*/private String getClientIp(HttpServletRequest request) {String ip = request.getHeader("X-Forwarded-For");if (ip != null && ip.length() != 0 && !"unknown".equalsIgnoreCase(ip)) {// 多次反向代理后會有多個ip值,第一個ip才是真實ipif (ip.contains(",")) {ip = ip.split(",")[0];}}if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {ip = request.getHeader("Proxy-Client-IP");}if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {ip = request.getHeader("WL-Proxy-Client-IP");}if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {ip = request.getHeader("HTTP_CLIENT_IP");}if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {ip = request.getHeader("HTTP_X_FORWARDED_FOR");}if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {ip = request.getRemoteAddr();}return ip;}/*** 獲取當前用戶ID(需要根據實際情況實現)*/private String getCurrentUserId() {// 實現獲取當前用戶ID的邏輯// 可以從Session、Token或Spring Security上下文等獲取return null;}
}

4. 創建全局異常處理器

import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;@RestControllerAdvice
public class GlobalExceptionHandler {@ExceptionHandler(RuntimeException.class)public Result handleRuntimeException(RuntimeException e) {return Result.error(e.getMessage());}
}

5. 在Controller中使用注解

import org.springframework.web.bind.annotation.*;@RestController
@RequestMapping("/api")
public class DemoController {// 基于IP的限制:60秒內最多訪問10次@AccessLimit(time = 60, maxCount = 10, checkIp = true, checkUser = false)@GetMapping("/public/data")public String getPublicData() {return "這是公開數據";}// 基于用戶的限制:30秒內最多訪問5次@AccessLimit(time = 30, maxCount = 5, checkIp = false, checkUser = true)@GetMapping("/user/data")public String getUserData() {return "這是用戶數據";}// 同時基于IP和用戶的限制:60秒內最多訪問3次@AccessLimit(time = 60, maxCount = 3, checkIp = true, checkUser = true)@PostMapping("/submit")public String submitData(@RequestBody String data) {return "提交成功: " + data;}
}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/98058.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/98058.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/98058.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

代碼隨想錄二刷之“貪心算法”~GO

簡單題目 1.455. 分發餅干 - 力扣&#xff08;LeetCode&#xff09; func findContentChildren(g []int, s []int) int {sort.Ints(g)sort.Ints(s)index : 0for i : 0;i<len(s);i{if index < len(g) && g[index] < s[i]{index}}return index }感悟&#xff…

Pod自動重啟問題排查:JDK 17 EA版本G1GC Bug導致的應用崩潰

Pod自動重啟問題排查:JDK 17 EA版本G1GC Bug導致的應用崩潰 問題背景 在生產環境中,我們遇到了一個嚴重的穩定性問題:應用Pod頻繁自動重啟,導致服務不穩定。通過深入分析JVM崩潰日志,最終定位到是JDK 17 EA版本中G1GC的一個已知Bug導致的。 問題現象 1. Pod重啟表現 應…

HTML文本格式化標簽

HTML提供了多種標簽用于文本的格式化&#xff0c;這些標簽可以改變文本的外觀&#xff08;如粗細、斜體&#xff09;或賦予文本特定的含義&#xff08;如強調、引用&#xff09;。1. 基本文本樣式標簽&#xff08;1&#xff09;粗體文本使用<b>或<strong>標簽可以使…

數據結構之單鏈表和環形鏈表的應用(二)-

目錄一、相交鏈表二、環形鏈表I三、環形鏈表II總結一、相交鏈表 相交鏈表 首先理解什么是鏈表相交&#xff0c;相交即存在共用的節點&#xff0c;鏈表相交有三種情況&#xff0c; 中間位置相交頭部就開始相交尾部相交 如圖pcurA和pcurB就都有一個next指針指向同一個節點 這…

屬性關鍵字

屬性關鍵字深拷貝與淺拷貝類型各類對象深淺拷貝判斷完全深拷貝的實現屬性關鍵字property、synthesize和dynamic原子操作讀寫權限內存管理strong &#x1f19a; copy總結深拷貝與淺拷貝 先前學習OC時已經對深淺拷貝進行了一次學習&#xff0c;這里進行一個復習總結和補充&#…

突發奇想,還未實踐,在Vben5的Antd模式下,將表單從「JS 配置化」改寫成「模板可視化」形式(豆包版)

在 Vben5 的 Antd 模式下&#xff0c;完全可以將表單從「JS 配置化」改寫成「模板可視化」形式&#xff0c;把表單項直接寫在 Vue 模板中&#xff0c;更直觀且符合傳統 Vue 開發習慣。以下是完整的改寫示例&#xff0c;保留原功能但結構更清晰&#xff1a; 改寫思路 放棄 JS 中…

【更新完畢】2025數學建模國賽E題思路代碼文章高教社杯全國大學生數學建模-AI 輔助智能體測

全部更新完畢 包含完整的文章全部問題的代碼、結果、圖表 完整內容請看文末最后的推廣群基于AI姿態識別的立定跳遠運動分析與個性化訓練優化研究 隨著《國家學生體質健康標準》的頒布實施&#xff0c;通過AI技術輔助體育運動分析已成為提升學生體質健康水平的重要手段。本研究針…

小白友好,無需基礎也能快速上手的AI部署工具,一鍵部署

AI大模型相信已經成為許多人工作和生活中的得力助手。然而&#xff0c;對于大多數普通用戶而言&#xff0c;將強大的AI模型部署到自己的電腦上&#xff0c;似乎是一項遙不可及的技術活&#xff0c;往往涉及到復雜的命令行操作、環境配置和代碼調試。那有沒有一種工具&#xff0…

《Python復刻植物大戰僵尸開源項目實戰:Pygame框架+JSON關卡設計,解鎖塔防游戲開發新技能》?

&#x1f4cc; 大家好&#xff0c;我是智界工具庫&#xff0c;每天分享好用實用且智能的開源項目&#xff0c;以及在JAVA語言開發中遇到的問題&#xff0c;如果本篇文章對您有所幫助&#xff0c;請幫我點個小贊小收藏小關注吧&#xff0c;謝謝喲&#xff01;&#x1f618; 博主…

CCS——將工程中的 include / lib 修改為相對路徑,方便工程分享

在使用 Code Composer Studio (CCS) 開發 DSP 或 ARM 工程時&#xff0c;經常會遇到這樣一個問題&#xff1a;在 A 電腦上能正常編譯的工程&#xff0c;拷貝到 B 電腦上后就報錯。錯誤的原因通常是 工程使用了絕對路徑&#xff0c;而不同電腦上的文件路徑不一致&#xff0c;比如…

java解析網絡大端、小端解析方法

文章目錄一、背景介紹二、說明核心概念&#xff1a;什么是字節序&#xff08;Endianness&#xff09;&#xff1f;大端字節序 (Big-Endian)小端字節序 (Little-Endian)三、不同解析方式介紹一、背景介紹 中轉臺通過SNMP協議V1\V2上報中轉臺IP&#xff0c;然后程序解析入庫&…

【數據分享】土地利用矢量shp數據分享-甘肅

今天要說明數據就是土地利用shp數據分享-甘肅。數據介紹▲ 1km土地利用數據&#xff08;2020年&#xff09;▲ 土地利用數據&#xff08;2025年&#xff09;▲土地利用數據&#xff08;2018年&#xff09;▲ 30m土地利用數據&#xff08;2023年&#xff09;▲ 公路鐵路道路河流…

java log相關:Log4J、Log4J2、LogBack,SLF4J

目錄測試maven依賴logback.xml測試主程序測試輸出arthas查看logger總結使用參考文檔測試 maven依賴 <dependencies><!-- SLF4J API --><dependency><groupId>org.slf4j</groupId><artifactId>slf4j-api</artifactId><version>…

AES加密算法詳細加密步驟代碼實現--身份證號碼加解密系統

系統概述 本系統是一個基于AES-256-CBC加密算法的身份證號碼加解密工具&#xff08;手搓底層步驟&#xff09;&#xff0c;針對的是上一篇文章對的AES加密原理的講解&#xff0c;雖說是演示&#xff0c;但功能完善&#xff0c;可單獨提供接口給項目調用&#xff0c;采用Python…

LangChain: Models, Prompts 模型和提示詞

獲取openapikey #!pip install python-dotenv #!pip install openai import osimport openai ? from dotenv import load_dotenv, find_dotenv _ load_dotenv(find_dotenv()) # read local .env file openai.api_key os.environ[OPENAI_API_KEY] # account for deprecat…

ACMESSL自動續簽教程

目錄 1、選擇申請證書 ?編輯2、選擇CA機構 ?編輯3、選擇自動驗簽 ?編輯4、證書續簽設置 5、自動發布設置 本教程實現ACMESSL自動續簽&#xff0c;請按照此教程實現。 1、選擇申請證書 點擊快捷入口或者訂單或證書列表中的【創建證書】按鈕&#xff1a; 2、選擇CA機構 …

基于飛算JavaAI的在線圖書借閱平臺設計實現

項目概述與需求分析 1.1 項目背景與意義 隨著數字化時代的快速發展&#xff0c;傳統圖書館管理模式已無法滿足現代讀者的需求。在線圖書借閱平臺通過互聯網技術將圖書資源數字化&#xff0c;為讀者提供便捷的檢索、借閱和管理服務&#xff0c;有效解決了傳統圖書館開放時間有…

通過API接口管理企業微信通訊錄案例

1.開始前需要登錄企業微信管理員后臺&#xff0c;開啟通訊錄同步&#xff0c;同時添加企業可信IP地址&#xff0c;記錄下Secret信息和企業ID&#xff0c;后面的程序會用到這兩個參數。2.下面是用python寫的創建企業微信賬號的具體案例。#!/usr/bin/env python3 # -*- coding: u…

硬件開發_基于物聯網的自動售賣機系統

一.系統概述 物聯網自動售賣機系統的主要功能如下&#xff1a; 核心控制器&#xff1a;采用STM32單片機作為系統核心&#xff0c;負責整體數據處理和各設備的統一控制。商品選擇&#xff1a;支持語音識別及按鍵方式&#xff0c;方便用戶在售賣機內選擇商品。語音播報&#xff1…

AGENTS.md: AI編碼代理的開放標準

每個項目都有一個 README.md 文件供人類閱讀。但隨著 AI 編碼代理和 AI 輔助開發的興起,我們需要一個新標準:AGENTS.md。這個 Markdown 文件定義了代理如何構建、測試和協作。 這就是 AGENTS.md 的作用。 它是一個簡單的 Markdown 文件,告訴 AI 助手如何在你的項目中操作:…