在线观看www成人影院-在线观看www日本免费网站-在线观看www视频-在线观看操-欧美18在线-欧美1级

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

分享一種優雅的接口防刷處理方案

jf_ro2CN3Fa ? 來源:稀土掘金 ? 2023-03-29 14:56 ? 次閱讀

原理

通過ip地址+uri拼接用以作為訪問者訪問接口區分

通過在Interceptor中攔截請求,從Redis中統計用戶訪問接口次數從而達到接口防刷目的

如下圖所示

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

工程

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

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

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

/**
*多長時間內
*/
@Value("${interfaceAccess.second}")
privateLongsecond=10L;

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

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

/**
*鎖住時的key前綴
*/
publicstaticfinalStringLOCK_PREFIX="LOCK";

/**
*統計次數時的key前綴
*/
publicstaticfinalStringCOUNT_PREFIX="COUNT";


publicbooleanpreHandle(HttpServletRequestrequest,HttpServletResponseresponse,Objecthandler)throwsException{

Stringuri=request.getRequestURI();
Stringip=request.getRemoteAddr();//這里忽略代理軟件方式訪問,默認直接訪問,也就是獲取得到的就是訪問者真實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)){
//首次訪問
log.info("首次訪問");
redisTemplate.opsForValue().set(countKey,1,second,TimeUnit.SECONDS);
}else{
//此用戶前一點時間就訪問過該接口
if((Integer)count

在多長時間內訪問接口多少次,以及禁用的時長,則是通過與配置文件配合動態設置

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

當處于禁用時直接拋異常則是通過在ControllerAdvice處統一處理 (這里代碼寫的有點丑陋)

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

下面是一些測試(可以把項目通過Git還原到“【初始化】”狀態進行測試)

正常訪問時

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

訪問次數過于頻繁時

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

自我提問

上述實現就好像就已經達到了我們的接口防刷目的了

但是,還不夠

為方便后續描述,項目中新增補充Controller,如下所示

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

簡單來說就是

PassCotroller和RefuseController

每個Controller分別有對應的get,post,put,delete類型的方法,其映射路徑與方法名稱一致

接口自由

對于上述實現,不知道你們有沒有發現一個問題

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

而在實際開發中,說對于所有的接口都要做防刷處理,感覺上也不太可能(寫此文時目前大四,實際工作經驗較少,這里不敢肯定)

那么問題有了,該如何解決呢?目前來說想到兩個解決方案

攔截器映射規則

項目通過Git還原到"【Interceptor設置映射規則實現接口自由】"版本即可得到此案例實現

我們都知道攔截器是可以設置攔截規則的,從而達到攔截處理目的

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

1.這個AccessInterfaceInterceptor是專門用來進行防刷處理的,那么實際上我們可以通過設置它的映射規則去匹配需要進行【接口防刷】的接口即可

2.比如說下面的映射配置

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

3.這樣就初步達到了我們的目的,通過映射規則的配置,只針對那些需要進行【接口防刷】的接口才會進行處理

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

所有要進行防刷處理的接口統一都是配置成了 x 秒內 y 次訪問次數,禁用時長為 z 秒

要知道就是要進行防刷處理的接口,其 x, y, z的值也是并不一定會統一的

某些防刷接口處理比較消耗性能的,我就把x, y, z設置的緊一點

而某些防刷接口處理相對來說比較快,我就把x, y, z 設置的松一點

這沒問題吧

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

這就是其中一個不足點

當然,其實針對當前這種情況也有解決方案

那就是弄多個攔截器

每個攔截器的【接口防刷】處理邏輯跟上述一致,并去映射對應要處理的防刷接口

唯一不同的就是在每個攔截器內部,去修改對應防刷接口需要的x, y, z值

這樣就是感覺會比較麻煩

防刷接口映射路徑修改后維護問題

雖然說防刷接口的映射路徑基本上定下來后就不會改變

但實際上前后端聯調開發項目時,不會有那么嚴謹的Api文檔給我們用(這個在實習中倒是碰到過,公司不是很大,開發起來也就不那么嚴謹,啥都要自己搞,功能能實現就好)

也就是說還是會有那種要修改接口的映射路徑需求

當防刷接口數量特別多,后面的接手人員就很痛苦了

就算是項目是自己從0到1實現的,其實有時候項目開發到后面,自己也會忘記自己前面是如何設計的

而使用當前這種方式的話,誰維護誰蛋疼

自定義注解 + 反射

咋說呢

就是通過自定義注解中定義 x 秒內 y 次訪問次數,禁用時長為 z 秒

自定義注解 + 在需要進行防刷處理的各個接口方法上

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

下面做個實現

聲明自定義注解

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

Controlller中方法中使用

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

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

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

/**
*統計次數時的key前綴
*/
publicstaticfinalStringCOUNT_PREFIX="COUNT";


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

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

由于不好演示效果,這里就不貼測試結果圖片了

項目通過Git還原到"【自定義主鍵+反射實現接口自由"版本即可得到此案例實現,后面自己可以針對接口做下測試看看是否如同我所說的那樣實現自定義x, y, z 的效果

嗯,現在看起來,可以針對每個要進行防刷處理的接口進行針對性自定義多長時間內的最大訪問次數,以及禁用時長,哪個接口需要,就直接+在那個接口方法出即可

感覺還不錯的樣子,現在網上挺多資料也都是這樣實現的

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

先舉一個例子,以我們的PassController為例,如下是其實現

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

下圖是其映射路徑關系

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

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

我們在類上通過注解@ReqeustMapping標記映射路徑/pass,這樣所有的接口方法前綴都包含了/pass,并且以致于后面要修改映射路徑前綴時只需改這一塊地方即可

這也是我們使用SpringMVC最常見的用法

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

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

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

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

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

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

AccessLimitInterceptor中代碼修改的有點多,主要邏輯如下

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

與之前實現比較,不同點在于x, y, z的值要首先嘗試在目標類中獲取

其次,一旦類中標有此注解,即代表此類下所有接口方法都要進行防刷處理

如果其接口方法同樣也標有此注解,根據就近優先原則,以接口方法中的注解標明的值為準

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

/**
*鎖住時的key前綴
*/
publicstaticfinalStringLOCK_PREFIX="LOCK";

/**
*統計次數時的key前綴
*/
publicstaticfinalStringCOUNT_PREFIX="COUNT";


publicbooleanpreHandle(HttpServletRequestrequest,HttpServletResponseresponse,Objecthandler)throwsException{

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

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

好了,這樣就達到我們想要的效果了

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

項目通過Git還原到"【自定義注解+反射實現接口自由-版本2.0】"版本即可得到此案例實現,自己可以測試萬一下

這是目前來說比較理想的做法,至于其他做法,暫時沒啥了解到

時間邏輯漏洞

這是我一開始都有留意到的問題

也是一直搞不懂,就是我們現在的所有做法其實感覺都不是嚴格意義上的x秒內y次訪問次數

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

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

假設我們固定某個接口5秒內只能訪問3次,以下面例子為例

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

底下的小圓圈代表此刻請求訪問接口

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

第2秒請求到,為首次訪問,Redis中統計次數為1(過期時間為5秒)

第7秒,此時有兩個動作,一是請求到,二是剛剛第二秒Redis存的值現在過期

我們先假設這一刻,請求處理完后,Redis存的值才過期

按照這樣的邏輯走

第七秒請求到,Redis存在對應key,且不大于3, 次數+1

接著這個key立馬過期

再繼續往后走,第8秒又當做新的一個起始,就不往下說了,反正就是不會出現禁用的情況

按照上述邏輯走,實際上也就是說當出現首次訪問時,當做這5秒時間片段的起始

第2秒是,第8秒也是

但是有沒有想過,實際上這個5秒時間片段實際上是可以放置在時間軸上任意區域的

上述情況我們是根據請求的到來情況人為的把它放在【2-7】,【8-13】上

而實際上這5秒時間片段是可以放在任意區域的

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

而【7-12】這段時間有4次請求,就達到了我們禁用的條件了

是不是感覺怪怪的

想過其他做法,但是好像嚴格意義上真的做不到我所說的那樣(至少目前來說想不到)

之前我們的做法,正常來說也夠用,至少說有達到防刷的作用

后面有機會的話再看看,不知道我是不是鉆牛角尖了

路徑參數問題

假設現在PassController中有如下接口方法

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

也就是我們在接口方法中常用的在請求路徑中獲取參數的套路

但是使用路徑參數的話,就會發生問題

那就是同一個ip地址訪問此接口時,我攜帶的參數值不同

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

下圖是訪問此接口,攜帶不同參數值時獲取的uri狀況

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

這樣的話在我們之前攔截器的處理邏輯中,會認為是此ip用戶訪問的是不同的接口方法,而實際上訪問的是同一個接口方法

也就導致了【接口防刷】失效

接下來就是解決它,目前來說有兩種

不要使用路徑參數

這算是比較理想的做法,相當于沒這個問題

但有一定局限性,有時候接手別的項目,或者自己根本沒這個權限說不能使用路徑參數

替換uri

我們獲取uri的目的,其實就是為了區別訪問接口

而把uri替換成另一種可以區分訪問接口方法的標識即可

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

當然,其實不同的Controller中,其接口方法名稱也有可能是相同的

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

實際解決方案有很多,看個人需求吧

真實ip獲取

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

但是后續有了解到,如果說通過代理軟件方式訪問的話,這樣是獲取不到來訪者的真實ip的

至于如何獲取,后續我再研究下http再說,這里先提個醒

總結

說實話,挺有意思的,一開始自己想【接口防刷】的時候,感覺也就是轉化成統計下訪問次數的問題擺了。后面到網上看別人的寫法,又再自己給自己找點問題出來,后面會衍生出來一推東西出來,諸如自定義注解+反射這種實現方式。

以前其實對注解 + 反射其實有點不太懂干嘛用的,而從之前的數據報表導出,再到基本權限控制實現,最后到今天的【接口防刷】一點點來進步去補充自己的知識點,而且,感覺寫博客真的是件挺有意義的事情,它會讓你去更深入的了解某個點,并且知識是相關聯的,探索的過程中會牽扯到其他別的知識點,就像之前的寫的【單例模式】實現,一開始就了解到懶漢式,餓漢式

后面深入的話就知道其實會還有序列化/反序列化,反射調用生成實例,對象克隆這幾種方式回去破壞單例模式,又是如何解決的,這也是一個進步的點,后續為了保證線程安全問題,牽扯到的synchronized,voliate關鍵字,繼而又關聯到JVM,JUC,操作系統的東西。






審核編輯:劉清

聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • 計數器
    +關注

    關注

    32

    文章

    2256

    瀏覽量

    94568
  • MVC
    MVC
    +關注

    關注

    0

    文章

    73

    瀏覽量

    13859
  • API接口
    +關注

    關注

    1

    文章

    84

    瀏覽量

    10438
  • Redis
    +關注

    關注

    0

    文章

    375

    瀏覽量

    10877

原文標題:優雅的接口防刷處理方案

文章出處:【微信號:芋道源碼,微信公眾號:芋道源碼】歡迎添加關注!文章轉載請注明出處。

收藏 人收藏

    評論

    相關推薦

    一種低功耗以太網接口電路的設計方案

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

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

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

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

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

    一種低成本高速USB接口的設計方案

    一種基于DSP平臺的低成本高速USB接口方案
    發表于 05-10 07:13

    分享一種智能卡接口的設計方案

    分享一種智能卡接口的設計方案
    發表于 05-27 06:01

    請問怎樣去設計一種監獄安系統?

    為什么要設計一種監獄安系統?怎樣去設計一種監獄安系統?如何對監獄安系統進行仿真測試?
    發表于 05-31 06:02

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

    折疊式手機面臨哪些問題?一種滿足手機高速圖像數據傳輸的差分串行接口方案
    發表于 06-01 06:51

    如何去實現一種高速通信接口的設計?

    一種FPGA與DSP的高速通信接口設計與實現方案
    發表于 06-02 06:07

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

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

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

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

    一種基于GSM和Zigbee技術的無線安系統

    ,為安系統的建設提供了一種靈活、方便的無線解決方案。該系統具有良好的可擴展性和實用價值,可以實現全方位的安全監控與防護,而其最重要的點在于,該系統能夠通過無線網絡可靠地與用戶通信,
    發表于 12-01 09:36

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

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

    一種高速串行視頻接口TIDA-00137參考設計

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

    一種篡改的電能表

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

    優雅接口處理方案

    采用https協議可以將傳輸的明文進行加密,但是黑客仍然可以截獲傳輸的數據包,進步偽造請求進行重放攻擊。如果黑客使用特殊手段讓請求方設備使用了偽造的證書進行通信,那么https加密的內容也會被解密。
    的頭像 發表于 04-14 10:59 ?640次閱讀
    主站蜘蛛池模板: 伊人久久综合成人网小说| 特级全黄一级毛片视频| 色老头久久久久| 国产精品va一区二区三区| 99久久99久久精品国产| 第四色亚洲| 老色鬼久久综合第一| 天天射天天干天天插| 欧美性色生活片天天看99| 中文字幕视频一区| 色婷婷六月天| 在线观看精品国产入口| 欧美黑人巨大xxx猛交| 免费看的黄视频| 无夜精品久久久久久| 欧美资源在线| 午夜黄大色黄大片美女图片| 亚洲视频在线免费看| 亚洲艹| 日本一卡二卡≡卡四卡精品| 夜夜夜爽爽爽久久久| 特黄特色大片免费播放路01| 欧美激情综合| 九九福利| 日本一区二区不卡在线| 欧美成人性动漫在线观看| 日本黄色网址大全| 欧美亚洲在线| 精品欧美一区二区三区| 四虎h789fcom| 伊人成人在线观看| 97在线人人| 午夜视频黄| 国产小视频你懂的| hd性欧美| 玖玖精品国产| 亚洲性爱城| 欧美精品首页| 春宵福利网站| 欧美日韩一区二区三区视频在线观看 | 色网站免费在线观看|