0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

分享一種優(yōu)雅的接口防刷處理方案

jf_ro2CN3Fa ? 來(lái)源:稀土掘金 ? 2023-03-29 14:56 ? 次閱讀

原理

通過(guò)ip地址+uri拼接用以作為訪問(wèn)者訪問(wèn)接口區(qū)分

通過(guò)在Interceptor中攔截請(qǐng)求,從Redis中統(tǒng)計(jì)用戶訪問(wèn)接口次數(shù)從而達(dá)到接口防刷目的

如下圖所示

b7163ee8-cdfd-11ed-bfe3-dac502259ad0.jpg

工程

其中,Interceptor處代碼處理邏輯最為重要

b7264f7c-cdfd-11ed-bfe3-dac502259ad0.jpg

/**
*@author:Zero
*@time:2023/2/14
*@description:接口防刷攔截處理
*/
@Slf4j
publicclassAccessLimintInterceptorimplementsHandlerInterceptor{
@Resource
privateRedisTemplateredisTemplate;

/**
*多長(zhǎng)時(shí)間內(nèi)
*/
@Value("${interfaceAccess.second}")
privateLongsecond=10L;

/**
*訪問(wèn)次數(shù)
*/
@Value("${interfaceAccess.time}")
privateLongtime=3L;

/**
*禁用時(shí)長(zhǎng)--單位/秒
*/
@Value("${interfaceAccess.lockTime}")
privateLonglockTime=60L;

/**
*鎖住時(shí)的key前綴
*/
publicstaticfinalStringLOCK_PREFIX="LOCK";

/**
*統(tǒng)計(jì)次數(shù)時(shí)的key前綴
*/
publicstaticfinalStringCOUNT_PREFIX="COUNT";


publicbooleanpreHandle(HttpServletRequestrequest,HttpServletResponseresponse,Objecthandler)throwsException{

Stringuri=request.getRequestURI();
Stringip=request.getRemoteAddr();//這里忽略代理軟件方式訪問(wèn),默認(rèn)直接訪問(wèn),也就是獲取得到的就是訪問(wèn)者真實(shí)ip地址
StringlockKey=LOCK_PREFIX+ip+uri;
ObjectisLock=redisTemplate.opsForValue().get(lockKey);
if(Objects.isNull(isLock)){
//還未被禁用
StringcountKey=COUNT_PREFIX+ip+uri;
Objectcount=redisTemplate.opsForValue().get(countKey);
if(Objects.isNull(count)){
//首次訪問(wèn)
log.info("首次訪問(wèn)");
redisTemplate.opsForValue().set(countKey,1,second,TimeUnit.SECONDS);
}else{
//此用戶前一點(diǎn)時(shí)間就訪問(wèn)過(guò)該接口
if((Integer)count

在多長(zhǎng)時(shí)間內(nèi)訪問(wèn)接口多少次,以及禁用的時(shí)長(zhǎng),則是通過(guò)與配置文件配合動(dòng)態(tài)設(shè)置

b72b37da-cdfd-11ed-bfe3-dac502259ad0.jpg

當(dāng)處于禁用時(shí)直接拋異常則是通過(guò)在ControllerAdvice處統(tǒng)一處理 (這里代碼寫的有點(diǎn)丑陋)

b73270c2-cdfd-11ed-bfe3-dac502259ad0.jpg

下面是一些測(cè)試(可以把項(xiàng)目通過(guò)Git還原到“【初始化】”狀態(tài)進(jìn)行測(cè)試)

正常訪問(wèn)時(shí)

b73827c4-cdfd-11ed-bfe3-dac502259ad0.jpgb74115be-cdfd-11ed-bfe3-dac502259ad0.jpg

訪問(wèn)次數(shù)過(guò)于頻繁時(shí)

b747cc60-cdfd-11ed-bfe3-dac502259ad0.jpgb752a1da-cdfd-11ed-bfe3-dac502259ad0.jpg

自我提問(wèn)

上述實(shí)現(xiàn)就好像就已經(jīng)達(dá)到了我們的接口防刷目的了

但是,還不夠

為方便后續(xù)描述,項(xiàng)目中新增補(bǔ)充Controller,如下所示

b759fe4e-cdfd-11ed-bfe3-dac502259ad0.jpg

簡(jiǎn)單來(lái)說(shuō)就是

PassCotroller和RefuseController

每個(gè)Controller分別有對(duì)應(yīng)的get,post,put,delete類型的方法,其映射路徑與方法名稱一致

接口自由

對(duì)于上述實(shí)現(xiàn),不知道你們有沒(méi)有發(fā)現(xiàn)一個(gè)問(wèn)題

就是現(xiàn)在我們的接口防刷處理,針對(duì)是所有的接口(項(xiàng)目案例中我只是寫的接口比較少)

而在實(shí)際開(kāi)發(fā)中,說(shuō)對(duì)于所有的接口都要做防刷處理,感覺(jué)上也不太可能(寫此文時(shí)目前大四,實(shí)際工作經(jīng)驗(yàn)較少,這里不敢肯定)

那么問(wèn)題有了,該如何解決呢?目前來(lái)說(shuō)想到兩個(gè)解決方案

攔截器映射規(guī)則

項(xiàng)目通過(guò)Git還原到"【Interceptor設(shè)置映射規(guī)則實(shí)現(xiàn)接口自由】"版本即可得到此案例實(shí)現(xiàn)

我們都知道攔截器是可以設(shè)置攔截規(guī)則的,從而達(dá)到攔截處理目的

b7652170-cdfd-11ed-bfe3-dac502259ad0.jpg

1.這個(gè)AccessInterfaceInterceptor是專門用來(lái)進(jìn)行防刷處理的,那么實(shí)際上我們可以通過(guò)設(shè)置它的映射規(guī)則去匹配需要進(jìn)行【接口防刷】的接口即可

2.比如說(shuō)下面的映射配置

b76b7aac-cdfd-11ed-bfe3-dac502259ad0.jpg

3.這樣就初步達(dá)到了我們的目的,通過(guò)映射規(guī)則的配置,只針對(duì)那些需要進(jìn)行【接口防刷】的接口才會(huì)進(jìn)行處理

4.至于為啥說(shuō)是初步呢?下面我就說(shuō)說(shuō)目前我想到的使用這種方式進(jìn)行【接口防刷】的不足點(diǎn):

所有要進(jìn)行防刷處理的接口統(tǒng)一都是配置成了 x 秒內(nèi) y 次訪問(wèn)次數(shù),禁用時(shí)長(zhǎng)為 z 秒

要知道就是要進(jìn)行防刷處理的接口,其 x, y, z的值也是并不一定會(huì)統(tǒng)一的

某些防刷接口處理比較消耗性能的,我就把x, y, z設(shè)置的緊一點(diǎn)

而某些防刷接口處理相對(duì)來(lái)說(shuō)比較快,我就把x, y, z 設(shè)置的松一點(diǎn)

這沒(méi)問(wèn)題吧

但是現(xiàn)在呢?x, y, z值全都一致了,這就不行了

這就是其中一個(gè)不足點(diǎn)

當(dāng)然,其實(shí)針對(duì)當(dāng)前這種情況也有解決方案

那就是弄多個(gè)攔截器

每個(gè)攔截器的【接口防刷】處理邏輯跟上述一致,并去映射對(duì)應(yīng)要處理的防刷接口

唯一不同的就是在每個(gè)攔截器內(nèi)部,去修改對(duì)應(yīng)防刷接口需要的x, y, z值

這樣就是感覺(jué)會(huì)比較麻煩

防刷接口映射路徑修改后維護(hù)問(wèn)題

雖然說(shuō)防刷接口的映射路徑基本上定下來(lái)后就不會(huì)改變

但實(shí)際上前后端聯(lián)調(diào)開(kāi)發(fā)項(xiàng)目時(shí),不會(huì)有那么嚴(yán)謹(jǐn)?shù)腁pi文檔給我們用(這個(gè)在實(shí)習(xí)中倒是碰到過(guò),公司不是很大,開(kāi)發(fā)起來(lái)也就不那么嚴(yán)謹(jǐn),啥都要自己搞,功能能實(shí)現(xiàn)就好)

也就是說(shuō)還是會(huì)有那種要修改接口的映射路徑需求

當(dāng)防刷接口數(shù)量特別多,后面的接手人員就很痛苦了

就算是項(xiàng)目是自己從0到1實(shí)現(xiàn)的,其實(shí)有時(shí)候項(xiàng)目開(kāi)發(fā)到后面,自己也會(huì)忘記自己前面是如何設(shè)計(jì)的

而使用當(dāng)前這種方式的話,誰(shuí)維護(hù)誰(shuí)蛋疼

自定義注解 + 反射

咋說(shuō)呢

就是通過(guò)自定義注解中定義 x 秒內(nèi) y 次訪問(wèn)次數(shù),禁用時(shí)長(zhǎng)為 z 秒

自定義注解 + 在需要進(jìn)行防刷處理的各個(gè)接口方法上

在攔截器中通過(guò)反射獲取到各個(gè)接口中的x, y, z值即可達(dá)到我們想要的接口自由目的

下面做個(gè)實(shí)現(xiàn)

聲明自定義注解

b770cfc0-cdfd-11ed-bfe3-dac502259ad0.jpg

Controlller中方法中使用

b7795514-cdfd-11ed-bfe3-dac502259ad0.jpg

Interceptor處邏輯修改(最重要是通過(guò)反射判斷此接口是否需要進(jìn)行防刷處理,以及獲取到x, y, z的值)

/**
*@author:Zero
*@time:2023/2/14
*@description:接口防刷攔截處理
*/
@Slf4j
publicclassAccessLimintInterceptorimplementsHandlerInterceptor{
@Resource
privateRedisTemplateredisTemplate;
/**
*鎖住時(shí)的key前綴
*/
publicstaticfinalStringLOCK_PREFIX="LOCK";

/**
*統(tǒng)計(jì)次數(shù)時(shí)的key前綴
*/
publicstaticfinalStringCOUNT_PREFIX="COUNT";


publicbooleanpreHandle(HttpServletRequestrequest,HttpServletResponseresponse,Objecthandler)throwsException{
//自定義注解+反射實(shí)現(xiàn)
//判斷訪問(wèn)的是否是接口方法
if(handlerinstanceofHandlerMethod){
//訪問(wèn)的是接口方法,轉(zhuǎn)化為待訪問(wèn)的目標(biāo)方法對(duì)象
HandlerMethodtargetMethod=(HandlerMethod)handler;
//取出目標(biāo)方法中的AccessLimit注解
AccessLimitaccessLimit=targetMethod.getMethodAnnotation(AccessLimit.class);
//判斷此方法接口是否要進(jìn)行防刷處理(方法上沒(méi)有對(duì)應(yīng)注解就代表不需要,不需要的話進(jìn)行放行)
if(!Objects.isNull(accessLimit)){
//需要進(jìn)行防刷處理,接下來(lái)是處理邏輯
Stringip=request.getRemoteAddr();
Stringuri=request.getRequestURI();
StringlockKey=LOCK_PREFIX+ip+uri;
ObjectisLock=redisTemplate.opsForValue().get(lockKey);
//判斷此ip用戶訪問(wèn)此接口是否已經(jīng)被禁用
if(Objects.isNull(isLock)){
//還未被禁用
StringcountKey=COUNT_PREFIX+ip+uri;
Objectcount=redisTemplate.opsForValue().get(countKey);
longsecond=accessLimit.second();
longmaxTime=accessLimit.maxTime();

if(Objects.isNull(count)){
//首次訪問(wèn)
log.info("首次訪問(wèn)");
redisTemplate.opsForValue().set(countKey,1,second,TimeUnit.SECONDS);
}else{
//此用戶前一點(diǎn)時(shí)間就訪問(wèn)過(guò)該接口,且頻率沒(méi)超過(guò)設(shè)置
if((Integer)count

由于不好演示效果,這里就不貼測(cè)試結(jié)果圖片了

項(xiàng)目通過(guò)Git還原到"【自定義主鍵+反射實(shí)現(xiàn)接口自由"版本即可得到此案例實(shí)現(xiàn),后面自己可以針對(duì)接口做下測(cè)試看看是否如同我所說(shuō)的那樣實(shí)現(xiàn)自定義x, y, z 的效果

嗯,現(xiàn)在看起來(lái),可以針對(duì)每個(gè)要進(jìn)行防刷處理的接口進(jìn)行針對(duì)性自定義多長(zhǎng)時(shí)間內(nèi)的最大訪問(wèn)次數(shù),以及禁用時(shí)長(zhǎng),哪個(gè)接口需要,就直接+在那個(gè)接口方法出即可

感覺(jué)還不錯(cuò)的樣子,現(xiàn)在網(wǎng)上挺多資料也都是這樣實(shí)現(xiàn)的

但是還是可以有改善的地方

先舉一個(gè)例子,以我們的PassController為例,如下是其實(shí)現(xiàn)

b784144a-cdfd-11ed-bfe3-dac502259ad0.jpg

下圖是其映射路徑關(guān)系

b78dbaae-cdfd-11ed-bfe3-dac502259ad0.jpg

同一個(gè)Controller的所有接口方法映射路徑的前綴都包含了/pass

我們?cè)陬惿贤ㄟ^(guò)注解@ReqeustMapping標(biāo)記映射路徑/pass,這樣所有的接口方法前綴都包含了/pass,并且以致于后面要修改映射路徑前綴時(shí)只需改這一塊地方即可

這也是我們使用SpringMVC最常見(jiàn)的用法

那么,我們的自定義注解也可不可以這樣做呢?先無(wú)中生有個(gè)需求

假設(shè)PassController中所有接口都是要進(jìn)行防刷處理的,并且他們的x, y, z值就一樣

如果我們的自定義注解還是只能加載方法上的話,一個(gè)一個(gè)接口加,那么無(wú)疑這是一種很呆的做法

要改的話,其實(shí)也很簡(jiǎn)單,首先是修改自定義注解,讓其可以作用在類上

b794bb60-cdfd-11ed-bfe3-dac502259ad0.jpg

接著就是修改AccessLimitInterceptor的處理邏輯

AccessLimitInterceptor中代碼修改的有點(diǎn)多,主要邏輯如下

b79b63fc-cdfd-11ed-bfe3-dac502259ad0.jpg

與之前實(shí)現(xiàn)比較,不同點(diǎn)在于x, y, z的值要首先嘗試在目標(biāo)類中獲取

其次,一旦類中標(biāo)有此注解,即代表此類下所有接口方法都要進(jìn)行防刷處理

如果其接口方法同樣也標(biāo)有此注解,根據(jù)就近優(yōu)先原則,以接口方法中的注解標(biāo)明的值為準(zhǔn)

/**
*@author:Zero
*@time:2023/2/14
*@description:接口防刷攔截處理
*/
@Slf4j
publicclassAccessLimintInterceptorimplementsHandlerInterceptor{
@Resource
privateRedisTemplateredisTemplate;

/**
*鎖住時(shí)的key前綴
*/
publicstaticfinalStringLOCK_PREFIX="LOCK";

/**
*統(tǒng)計(jì)次數(shù)時(shí)的key前綴
*/
publicstaticfinalStringCOUNT_PREFIX="COUNT";


publicbooleanpreHandle(HttpServletRequestrequest,HttpServletResponseresponse,Objecthandler)throwsException{

//自定義注解+反射實(shí)現(xiàn),版本2.0
if(handlerinstanceofHandlerMethod){
//訪問(wèn)的是接口方法,轉(zhuǎn)化為待訪問(wèn)的目標(biāo)方法對(duì)象
HandlerMethodtargetMethod=(HandlerMethod)handler;
//獲取目標(biāo)接口方法所在類的注解@AccessLimit
AccessLimittargetClassAnnotation=targetMethod.getMethod().getDeclaringClass().getAnnotation(AccessLimit.class);
//特別注意不能采用下面這條語(yǔ)句來(lái)獲取,因?yàn)镾pring采用的代理方式來(lái)代理目標(biāo)方法
//也就是說(shuō)targetMethod.getClass()獲得是classorg.springframework.web.method.HandlerMethod,而不知我們真正想要的Controller
//AccessLimittargetClassAnnotation=targetMethod.getClass().getAnnotation(AccessLimit.class);
//定義標(biāo)記位,標(biāo)記此類是否加了@AccessLimit注解
booleanisBrushForAllInterface=false;
Stringip=request.getRemoteAddr();
Stringuri=request.getRequestURI();
longsecond=0L;
longmaxTime=0L;
longforbiddenTime=0L;
if(!Objects.isNull(targetClassAnnotation)){
log.info("目標(biāo)接口方法所在類上有@AccessLimit注解");
isBrushForAllInterface=true;
second=targetClassAnnotation.second();
maxTime=targetClassAnnotation.maxTime();
forbiddenTime=targetClassAnnotation.forbiddenTime();
}
//取出目標(biāo)方法中的AccessLimit注解
AccessLimitaccessLimit=targetMethod.getMethodAnnotation(AccessLimit.class);
//判斷此方法接口是否要進(jìn)行防刷處理
if(!Objects.isNull(accessLimit)){
//需要進(jìn)行防刷處理,接下來(lái)是處理邏輯
second=accessLimit.second();
maxTime=accessLimit.maxTime();
forbiddenTime=accessLimit.forbiddenTime();
if(isForbindden(second,maxTime,forbiddenTime,ip,uri)){
thrownewCommonException(ResultCode.ACCESS_FREQUENT);
}
}else{
//目標(biāo)接口方法處無(wú)@AccessLimit注解,但還要看看其類上是否加了(類上有加,代表針對(duì)此類下所有接口方法都要進(jìn)行防刷處理)
if(isBrushForAllInterface&&isForbindden(second,maxTime,forbiddenTime,ip,uri)){
thrownewCommonException(ResultCode.ACCESS_FREQUENT);
}
}
}
returntrue;
}

/**
*判斷某用戶訪問(wèn)某接口是否已經(jīng)被禁用/是否需要禁用
*
*@paramsecond多長(zhǎng)時(shí)間單位/秒
*@parammaxTime最大訪問(wèn)次數(shù)
*@paramforbiddenTime禁用時(shí)長(zhǎng)單位/秒
*@paramip訪問(wèn)者ip地址
*@paramuri訪問(wèn)的uri
*@returnture為需要禁用
*/
privatebooleanisForbindden(longsecond,longmaxTime,longforbiddenTime,Stringip,Stringuri){
StringlockKey=LOCK_PREFIX+ip+uri;//如果此ip訪問(wèn)此uri被禁用時(shí)的存在Redis中的key
ObjectisLock=redisTemplate.opsForValue().get(lockKey);
//判斷此ip用戶訪問(wèn)此接口是否已經(jīng)被禁用
if(Objects.isNull(isLock)){
//還未被禁用
StringcountKey=COUNT_PREFIX+ip+uri;
Objectcount=redisTemplate.opsForValue().get(countKey);
if(Objects.isNull(count)){
//首次訪問(wèn)
log.info("首次訪問(wèn)");
redisTemplate.opsForValue().set(countKey,1,second,TimeUnit.SECONDS);
}else{
//此用戶前一點(diǎn)時(shí)間就訪問(wèn)過(guò)該接口,且頻率沒(méi)超過(guò)設(shè)置
if((Integer)count

好了,這樣就達(dá)到我們想要的效果了

b7a014d8-cdfd-11ed-bfe3-dac502259ad0.jpg

項(xiàng)目通過(guò)Git還原到"【自定義注解+反射實(shí)現(xiàn)接口自由-版本2.0】"版本即可得到此案例實(shí)現(xiàn),自己可以測(cè)試萬(wàn)一下

這是目前來(lái)說(shuō)比較理想的做法,至于其他做法,暫時(shí)沒(méi)啥了解到

時(shí)間邏輯漏洞

這是我一開(kāi)始都有留意到的問(wèn)題

也是一直搞不懂,就是我們現(xiàn)在的所有做法其實(shí)感覺(jué)都不是嚴(yán)格意義上的x秒內(nèi)y次訪問(wèn)次數(shù)

特別注意這個(gè)x秒,它是連續(xù),任意的(代表這個(gè)x秒時(shí)間片段其實(shí)是可以發(fā)生在任意一個(gè)時(shí)間軸上)

我下面嘗試表達(dá)我的意思,但是我不知道能不能表達(dá)清楚

假設(shè)我們固定某個(gè)接口5秒內(nèi)只能訪問(wèn)3次,以下面例子為例

b7a8083c-cdfd-11ed-bfe3-dac502259ad0.jpg

底下的小圓圈代表此刻請(qǐng)求訪問(wèn)接口

按照我們之前所有做法的邏輯走

第2秒請(qǐng)求到,為首次訪問(wèn),Redis中統(tǒng)計(jì)次數(shù)為1(過(guò)期時(shí)間為5秒)

第7秒,此時(shí)有兩個(gè)動(dòng)作,一是請(qǐng)求到,二是剛剛第二秒Redis存的值現(xiàn)在過(guò)期

我們先假設(shè)這一刻,請(qǐng)求處理完后,Redis存的值才過(guò)期

按照這樣的邏輯走

第七秒請(qǐng)求到,Redis存在對(duì)應(yīng)key,且不大于3, 次數(shù)+1

接著這個(gè)key立馬過(guò)期

再繼續(xù)往后走,第8秒又當(dāng)做新的一個(gè)起始,就不往下說(shuō)了,反正就是不會(huì)出現(xiàn)禁用的情況

按照上述邏輯走,實(shí)際上也就是說(shuō)當(dāng)出現(xiàn)首次訪問(wèn)時(shí),當(dāng)做這5秒時(shí)間片段的起始

第2秒是,第8秒也是

但是有沒(méi)有想過(guò),實(shí)際上這個(gè)5秒時(shí)間片段實(shí)際上是可以放置在時(shí)間軸上任意區(qū)域的

上述情況我們是根據(jù)請(qǐng)求的到來(lái)情況人為的把它放在【2-7】,【8-13】上

而實(shí)際上這5秒時(shí)間片段是可以放在任意區(qū)域的

那么,這樣的話,【7-12】也可以放置

而【7-12】這段時(shí)間有4次請(qǐng)求,就達(dá)到了我們禁用的條件了

是不是感覺(jué)怪怪的

想過(guò)其他做法,但是好像嚴(yán)格意義上真的做不到我所說(shuō)的那樣(至少目前來(lái)說(shuō)想不到)

之前我們的做法,正常來(lái)說(shuō)也夠用,至少說(shuō)有達(dá)到防刷的作用

后面有機(jī)會(huì)的話再看看,不知道我是不是鉆牛角尖了

路徑參數(shù)問(wèn)題

假設(shè)現(xiàn)在PassController中有如下接口方法

b7bd6c90-cdfd-11ed-bfe3-dac502259ad0.jpg

也就是我們?cè)诮涌诜椒ㄖ谐S玫脑谡?qǐng)求路徑中獲取參數(shù)的套路

但是使用路徑參數(shù)的話,就會(huì)發(fā)生問(wèn)題

那就是同一個(gè)ip地址訪問(wèn)此接口時(shí),我攜帶的參數(shù)值不同

按照我們之前那種前綴+ip+uri拼接的形式作為key的話,其實(shí)是區(qū)分不了的

下圖是訪問(wèn)此接口,攜帶不同參數(shù)值時(shí)獲取的uri狀況

b7c4e394-cdfd-11ed-bfe3-dac502259ad0.jpg

這樣的話在我們之前攔截器的處理邏輯中,會(huì)認(rèn)為是此ip用戶訪問(wèn)的是不同的接口方法,而實(shí)際上訪問(wèn)的是同一個(gè)接口方法

也就導(dǎo)致了【接口防刷】失效

接下來(lái)就是解決它,目前來(lái)說(shuō)有兩種

不要使用路徑參數(shù)

這算是比較理想的做法,相當(dāng)于沒(méi)這個(gè)問(wèn)題

但有一定局限性,有時(shí)候接手別的項(xiàng)目,或者自己根本沒(méi)這個(gè)權(quán)限說(shuō)不能使用路徑參數(shù)

替換uri

我們獲取uri的目的,其實(shí)就是為了區(qū)別訪問(wèn)接口

而把uri替換成另一種可以區(qū)分訪問(wèn)接口方法的標(biāo)識(shí)即可

最容易想到的就是通過(guò)反射獲取到接口方法名稱,使用接口方法名稱替換成uri即可

當(dāng)然,其實(shí)不同的Controller中,其接口方法名稱也有可能是相同的

實(shí)際上可以再獲取接口方法所在類類名,使用類名 + 方法名稱替換uri即可

實(shí)際解決方案有很多,看個(gè)人需求吧

真實(shí)ip獲取

在之前的代碼中,我們獲取代碼都是通過(guò)request.getRemoteAddr()獲取的

但是后續(xù)有了解到,如果說(shuō)通過(guò)代理軟件方式訪問(wèn)的話,這樣是獲取不到來(lái)訪者的真實(shí)ip的

至于如何獲取,后續(xù)我再研究下http再說(shuō),這里先提個(gè)醒

總結(jié)

說(shuō)實(shí)話,挺有意思的,一開(kāi)始自己想【接口防刷】的時(shí)候,感覺(jué)也就是轉(zhuǎn)化成統(tǒng)計(jì)下訪問(wèn)次數(shù)的問(wèn)題擺了。后面到網(wǎng)上看別人的寫法,又再自己給自己找點(diǎn)問(wèn)題出來(lái),后面會(huì)衍生出來(lái)一推東西出來(lái),諸如自定義注解+反射這種實(shí)現(xiàn)方式。

以前其實(shí)對(duì)注解 + 反射其實(shí)有點(diǎn)不太懂干嘛用的,而從之前的數(shù)據(jù)報(bào)表導(dǎo)出,再到基本權(quán)限控制實(shí)現(xiàn),最后到今天的【接口防刷】一點(diǎn)點(diǎn)來(lái)進(jìn)步去補(bǔ)充自己的知識(shí)點(diǎn),而且,感覺(jué)寫博客真的是件挺有意義的事情,它會(huì)讓你去更深入的了解某個(gè)點(diǎn),并且知識(shí)是相關(guān)聯(lián)的,探索的過(guò)程中會(huì)牽扯到其他別的知識(shí)點(diǎn),就像之前的寫的【單例模式】實(shí)現(xiàn),一開(kāi)始就了解到懶漢式,餓漢式

后面深入的話就知道其實(shí)會(huì)還有序列化/反序列化,反射調(diào)用生成實(shí)例,對(duì)象克隆這幾種方式回去破壞單例模式,又是如何解決的,這也是一個(gè)進(jìn)步的點(diǎn),后續(xù)為了保證線程安全問(wèn)題,牽扯到的synchronized,voliate關(guān)鍵字,繼而又關(guān)聯(lián)到JVM,JUC,操作系統(tǒng)的東西。






審核編輯:劉清

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問(wèn)題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • 計(jì)數(shù)器
    +關(guān)注

    關(guān)注

    32

    文章

    2259

    瀏覽量

    94848
  • MVC
    MVC
    +關(guān)注

    關(guān)注

    0

    文章

    73

    瀏覽量

    13888
  • API接口
    +關(guān)注

    關(guān)注

    1

    文章

    84

    瀏覽量

    10476
  • Redis
    +關(guān)注

    關(guān)注

    0

    文章

    377

    瀏覽量

    10905

原文標(biāo)題:優(yōu)雅的接口防刷處理方案

文章出處:【微信號(hào):芋道源碼,微信公眾號(hào):芋道源碼】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。

收藏 人收藏

    評(píng)論

    相關(guān)推薦

    一種低功耗以太網(wǎng)接口電路的設(shè)計(jì)方案

    能力。##AX88796C支持可變I/O工作電壓的SPI或Non-PCI接口,可以靈活選用不同的微處理器進(jìn)行以太網(wǎng)接口的電路設(shè)計(jì)。##本文以以太網(wǎng)控制芯片AX88796C為核心,提出一種
    發(fā)表于 01-14 11:11 ?8278次閱讀
    <b class='flag-5'>一種</b>低功耗以太網(wǎng)<b class='flag-5'>接口</b>電路的設(shè)計(jì)<b class='flag-5'>方案</b>

    一種低成本的 高速SRAM 替代解決方案

    本帖最后由 病友來(lái)看病 于 2017-7-11 23:13 編輯 SRAM是各種memory(SDRAM,DDR1/2/3/4, LPDDR/2/3/4)中最昂貴的一種存儲(chǔ)方案, 高速SRAM
    發(fā)表于 07-05 22:08

    一種PCIe接口的視頻采集解決方案

    一種PCIe接口的視頻采集解決方案。
    發(fā)表于 04-30 06:29

    一種低成本高速USB接口的設(shè)計(jì)方案

    一種基于DSP平臺(tái)的低成本高速USB接口方案
    發(fā)表于 05-10 07:13

    分享一種智能卡接口的設(shè)計(jì)方案

    分享一種智能卡接口的設(shè)計(jì)方案
    發(fā)表于 05-27 06:01

    請(qǐng)問(wèn)怎樣去設(shè)計(jì)一種監(jiān)獄安系統(tǒng)?

    為什么要設(shè)計(jì)一種監(jiān)獄安系統(tǒng)?怎樣去設(shè)計(jì)一種監(jiān)獄安系統(tǒng)?如何對(duì)監(jiān)獄安系統(tǒng)進(jìn)行仿真測(cè)試?
    發(fā)表于 05-31 06:02

    介紹一種差分串行接口方案

    折疊式手機(jī)面臨哪些問(wèn)題?一種滿足手機(jī)高速圖像數(shù)據(jù)傳輸?shù)牟罘执?b class='flag-5'>接口方案
    發(fā)表于 06-01 06:51

    如何去實(shí)現(xiàn)一種高速通信接口的設(shè)計(jì)?

    一種FPGA與DSP的高速通信接口設(shè)計(jì)與實(shí)現(xiàn)方案
    發(fā)表于 06-02 06:07

    一種基于TMS320C6xll接口的圖像獲取方案

    本文提出了一種基于TMS320C6xll接口的圖像獲取方案。
    發(fā)表于 06-03 06:53

    分享一種CH451與AMEG32的接口方案

    分享一種CH451與AMEG32的接口方案
    發(fā)表于 06-04 06:06

    一種基于GSM和Zigbee技術(shù)的無(wú)線安系統(tǒng)

    ,為安系統(tǒng)的建設(shè)提供了一種靈活、方便的無(wú)線解決方案。該系統(tǒng)具有良好的可擴(kuò)展性和實(shí)用價(jià)值,可以實(shí)現(xiàn)全方位的安全監(jiān)控與防護(hù),而其最重要的點(diǎn)在于,該系統(tǒng)能夠通過(guò)無(wú)線網(wǎng)絡(luò)可靠地與用戶通信,
    發(fā)表于 12-01 09:36

    分享一種基于littlevgl2rtt軟件包的RGB屏幕接口優(yōu)化方案

    DMA2D的方式,一種是中斷方式,一種是輪詢方式。如果使用中斷方式,需要添加DMA2D中斷處理函數(shù)。兩者的區(qū)別就是,采用輪詢方式的,則直等待DMA2D傳輸結(jié)束,然后通過(guò)函數(shù)
    發(fā)表于 06-07 14:57

    一種高速串行視頻接口TIDA-00137參考設(shè)計(jì)

    描述TIDA-00137 參考設(shè)計(jì)是一種高速串行視頻接口,通過(guò)此接口,可將采用 DVP (LVCMOS) 接口的遠(yuǎn)程汽車 WVGA TFT LCD 顯示屏連接到視頻
    發(fā)表于 09-19 07:05

    一種篡改的電能表

    一種篡改的電能表
    發(fā)表于 04-17 17:39 ?5次下載
    <b class='flag-5'>一種</b><b class='flag-5'>防</b>篡改的電能表

    優(yōu)雅接口處理方案!

    采用https協(xié)議可以將傳輸?shù)拿魑倪M(jìn)行加密,但是黑客仍然可以截獲傳輸?shù)臄?shù)據(jù)包,進(jìn)步偽造請(qǐng)求進(jìn)行重放攻擊。如果黑客使用特殊手段讓請(qǐng)求方設(shè)備使用了偽造的證書進(jìn)行通信,那么https加密的內(nèi)容也會(huì)被解密。
    的頭像 發(fā)表于 04-14 10:59 ?661次閱讀