SpringBoot+Redis實(shí)現(xiàn)接口防刷的示例代碼
場景描述:
在實(shí)際開發(fā)中,當(dāng)前端請求后臺時(shí),如果后端處理比較慢,但是用戶是不知情的,此時(shí)后端仍在處理,但是前端用戶以為沒點(diǎn)到,那么再次點(diǎn)擊又發(fā)起請求,就會導(dǎo)致在短時(shí)間內(nèi)有很多請求給到后臺,可能會出現(xiàn)后臺崩潰或者數(shù)據(jù)重復(fù)添加的問題。那么如何解決這個(gè)問題呢?
為了避免短時(shí)間內(nèi)對一個(gè)接口訪問,我們可以通過AOP+自定義注解+Redis的方式,在接口上加一個(gè)自定義注解,然后通過AOP的前置通知,在Redis中存入一個(gè)有效期的值,當(dāng)訪問接口時(shí)這個(gè)值還未過期,則返回提示信息給前端,以此來避免短時(shí)間內(nèi)對接口的方法。
本文以一個(gè)文件下載的接口為例:假設(shè)文件下載會在20S內(nèi)完成,當(dāng)?shù)谝淮蜗螺d的時(shí)候,在redis中存入一個(gè)key,并設(shè)置其過期時(shí)間為20s。當(dāng)后續(xù)發(fā)起多次請求的時(shí)候,提示:訪問過于頻繁。先準(zhǔn)備一個(gè)文件:


實(shí)現(xiàn)過程:
(1)創(chuàng)建一個(gè)自定義注解,其中包括兩個(gè)屬性,一個(gè)是key,一個(gè)是key在Redis中的有效時(shí)間
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface LimitAccess {
/**
* 限制訪問的key
* @return
*/
String key();
/**
* 限制訪問時(shí)間
* @return
*/
int times();
}
(2)創(chuàng)建對應(yīng)的切面
import com.example.demo.anno.LimitAccess;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import java.util.concurrent.TimeUnit;
/**
* AOP類(通知類)
*/
@Component
@Aspect
public class LimitAspect {
@Autowired
private RedisTemplate redisTemplate;
@Pointcut("@annotation(com.example.demo.anno.LimitAccess)")
public void pt(){};
@Around("pt()")
public Object aopAround(ProceedingJoinPoint pjp) throws Throwable {
// 獲取切入點(diǎn)上面的自定義注解
Signature signature = pjp.getSignature();
MethodSignature methodSignature = (MethodSignature) signature;
// 獲取方法上面的注解
LimitAccess limitAccess = methodSignature.getMethod().getAnnotation(LimitAccess.class);
// 獲取注解上面的屬性
int limit = limitAccess.times();
String key = limitAccess.key();
// 根據(jù)key去找Redis中的值
Object o = redisTemplate.opsForValue().get(key);
// 如果不存在,說明是首次訪問,存入Redis,過期時(shí)間為limitAccess中的time
if (o == null) {
redisTemplate.opsForValue().set(key, "", limit, TimeUnit.SECONDS);
// 執(zhí)行切入點(diǎn)的方法
return pjp.proceed();
} else {
// 如果存在,說明不是首次訪問,給出提示信息
return "訪問過于頻繁";
}
}
}
(3)在需要限制的接口上,加上注解,并設(shè)置key和限制訪問時(shí)間
@GetMapping("/download")
@LimitAccess(key = "download_key", times = 20)
public String downLoadFile(HttpServletRequest request, HttpServletResponse response) {
FileInputStream inputStream = null;
BufferedInputStream bufferedInputStream = null;
OutputStream outputStream = null;
try {
File file = ResourceUtils.getFile("classpath:template/show.txt");
if (file.exists()) {
String fileName = file.getName();
String mineType = request.getServletContext().getMimeType(fileName);
response.setContentType(mineType);
response.setHeader("content-type", "application/form-data");
response.setHeader("Content-disposition", "attachment; fileName=" + fileName);
inputStream = new FileInputStream(file);
bufferedInputStream = new BufferedInputStream(inputStream);
outputStream = response.getOutputStream();
int len = 0;
byte[] buff = new byte[1024];
while ((len = bufferedInputStream.read(buff)) != -1) {
outputStream.write(buff, 0, len);
}
} else {
return "下載的文件資源不存在";
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (inputStream != null) {
try {
inputStream.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
if (bufferedInputStream != null) {
bufferedInputStream.close();
}
if (outputStream != null) {
outputStream.flush();
outputStream.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
return "success";
}測試結(jié)果:
第一次訪問:

第二次訪問:

當(dāng)download_key過期后,則可以繼續(xù)下載!
到此這篇關(guān)于SpringBoot+Redis實(shí)現(xiàn)接口防刷的示例代碼的文章就介紹到這了,更多相關(guān)SpringBoot Redis接口防刷內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
IntelliJ IDEA中設(shè)置數(shù)據(jù)庫連接全局共享的步驟詳解
本文詳解IntelliJ IDEA數(shù)據(jù)庫連接全局共享設(shè)置,通過創(chuàng)建/選擇數(shù)據(jù)源并設(shè)為全局,實(shí)現(xiàn)多項(xiàng)目間統(tǒng)一連接,提升效率、減少配置錯(cuò)誤,感興趣的可以了解一下2025-07-07
Java中復(fù)雜的Synchronized關(guān)鍵字使用方法詳解
Synchronized關(guān)鍵字是一個(gè)種鎖,其有很多名字,例如重量級鎖、悲觀鎖、可重入鎖、、非公平、對象鎖等等,這篇文章主要給大家介紹了關(guān)于Java中復(fù)雜的Synchronized關(guān)鍵字使用方法的相關(guān)資料,需要的朋友可以參考下2024-01-01
Java編寫計(jì)算器的常見方法實(shí)例總結(jié)
這篇文章主要介紹了Java編寫計(jì)算器的常見方法,結(jié)合實(shí)例形式總結(jié)分析了Java實(shí)現(xiàn)計(jì)算器功能的常用方法,需要的朋友可以參考下2016-04-04
java?String到底有多長?String超出長度該如何解決
在Java中,由于字符串常量池的存在,String常量長度限制取決于String常量在常量池中的存儲大小,下面這篇文章主要給大家介紹了關(guān)于java?String到底有多長?String超出長度該如何解決的相關(guān)資料,需要的朋友可以參考下2023-01-01
spring cloud gateway請求跨域問題解決方案
這篇文章主要介紹了spring cloud gateway請求跨域問題解決方案,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-01-01
springboot2+es7使用RestHighLevelClient的示例代碼
本文主要介紹了springboot2+es7使用RestHighLevelClient的示例代碼,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2022-07-07

