成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

Cors跨域(一):深入理解跨域請求概念及其根因

開發 前端
本文用很長的篇幅介紹了Cors跨域資源共享的相關知識,并且用代碼做了示范,希望能助你通關Cors這個狗皮膏藥一樣粘著我們的硬核知識點。本文文字敘述較多,介紹了同源、跨域、Cors的幾乎所有概念,雖然略顯難啃,但這些是指導我們實踐的說明書。

[[405056]]

前言

你好,我是YourBatman。

做Web開發的小伙伴對“跨域”定并不陌生,像狗皮膏藥一樣粘著幾乎每位同學,對它可謂既愛又恨。跨域請求之于創業、小型公司來講是個頭疼的問題,因為這類企業還未沉淀出一套行之有效的、統一的解決方案。

讓人擔憂的是,據我了解不少程序員同學(不乏有高級開發)碰到跨域問題大都一頭霧水:

然后很自然的 用谷歌去百度一下搜索答案,但相關文章可能參差不齊、魚龍混雜。短則半天長則一天(包含改代碼、部署等流程)此問題才得以解決,一個“小小跨域”問題成功偷走你的寶貴時間。

既然跨域是個如此常見(特別是當下前后端分離的開發模式),因此深入理解CORS變得就異常的重要了(反倒前端工程師不用太了解),因此早在2019年我剛開始寫博客那會就有過較為詳細的系列文章:

現在把它搬到公眾號形成技術專欄,并且加點料,讓它更深、更全面、更系統的幫助到你,希望可以助你從此不再怕Cors跨域資源共享問題。

本文提綱

版本約定

  • JDK:8
  • Servlet:4.x

正文

文章遵循一貫的風格,本文將采用概念 + 代碼示例的方式,層層遞進的進行展開敘述。那么上菜,先來個示例預覽,模擬一下跨域請求,后面的一些的概念示例將以此作為抓手。

模擬跨域請求

要模擬跨域請求的根本是需要兩個源:讓請求的來源和目標源不一樣。這里我就使用IDEA作為靜態Web服務器(63342),Tomcat作為后端動態Servlet服務器(8080)。

❝說明:服務器都在本機,端口不一樣即可❞

前端代碼

  1. <!DOCTYPE html> 
  2. <html lang="en"
  3. <head> 
  4.     <meta charset="UTF-8"
  5.     <title>CORS跨域請求</title> 
  6.     <!--導入Jquery--> 
  7.     <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.js"></script> 
  8. </head> 
  9. <body> 
  10. <button id="btn">跨域從服務端獲取內容</button> 
  11. <div id="content"></div> 
  12.  
  13. <script> 
  14.     $("#btn").click(function () { 
  15.         // 跨域請求 
  16.         $.get("http://localhost:8080/cors"function (result) { 
  17.             $("#content").append(result).append("<br/>"); 
  18.         }); 
  19.  
  20.         // 同域請求 
  21.         $.get("http://localhost:63342"); 
  22.         $.post("http://localhost:63342"); 
  23.     }); 
  24.  
  25. </script> 
  26. </body> 
  27. </html> 

 使用IDEA作為靜態web服務器,瀏覽器輸入地址即可訪問(注:端口號為63342):

后端代碼

后端寫個Servlet來接收cors請求

  1. /** 
  2.  * 在此處添加備注信息 
  3.  * 
  4.  * @author YourBatman. <a href=mailto:yourbatman@aliyun.com>Send email to me</a> 
  5.  * @site https://yourbatman.cn 
  6.  * @date 2021/6/9 10:36 
  7.  * @since 0.0.1 
  8.  */ 
  9. @Slf4j 
  10. @WebServlet(urlPatterns = "/cors"
  11. public class CorsServlet extends HttpServlet { 
  12.  
  13.     @Override 
  14.     protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { 
  15.         String requestURI = req.getRequestURI(); 
  16.         String method = req.getMethod(); 
  17.         String originHeader = req.getHeader("Origin"); 
  18.  
  19.         log.info("收到請求:{},方法:{}, Origin頭:{}", requestURI, method, originHeader); 
  20.         resp.getWriter().write("hello cors..."); 
  21.     } 

啟動后端服務器,點擊頁面上的按鈕,結果如下:

服務端控制臺輸出:

  1. ... INFO  c.y.cors.servlet.CorsServlet - 收到請求:/cors,方法:GET, Origin頭:http://localhost:63342 

❝服務端輸出日志,說明即使前端的Http Status是error,但服務端還是收到并處理了這個請求的❞下面以此代碼示例為基礎,普及一下和Cors跨域資源共享相關的概念。

Host、Referer、Origin的區別

這哥三看起來很是相似,下面對概念作出區分。

1.Host:去哪里。域名+端口。值為客戶端將要訪問的遠程主機,瀏覽器在發送Http請求時會帶有此Header

2.Referer:來自哪里。協議+域名+端口+路徑+參數。當前請求的來源頁面的地址,服務端一般使用 Referer 首部識別訪問來源,可能會以此進行統計分析、日志記錄以及緩存優化等

  • 來源頁面協議為File或者Data URI(如頁面從本地打開的)
  • 來源頁面是Https,而目標URL是http
  • 瀏覽器地址欄直接輸入網址訪問,或者通過瀏覽器的書簽直接訪問
  • 使用JS的location.href跳轉
  • ...

常見應用場景:百度的搜索廣告就會分析Referer來判斷打開站點是從百度搜索跳轉的,還是直接URL輸入地址的

一般情況下瀏覽器會帶有此Header,但這些case不會帶有Referer這個頭

3.Origin:來自哪里(跨域)。協議+域名+端口。它用于Cors請求和同域POST請求

可以看到Referer與Origin功能相似,前者一般用于統計和阻止盜鏈,后者用于CORS請求。但是還是有幾點不同:

1.只有跨域請求,或者同域時發送post請求,才會攜帶Origin請求頭;而Referer只要瀏覽器能獲取到都會攜帶(除了上面說明的幾種case外)

2.若瀏覽器不能獲取到請求源頁面地址(如上面的幾種case),Referer頭不會發送,但Origin依舊會發送,只是值是null而已(注:雖然值為null,但此請求依舊屬于Cors請求哦),如下圖所示:

3.Origin的值只包括協議、域名和端口,而Rerferer不但包括協議、域名、端口還包括路徑,參數,注意不包括hash值

瀏覽器的同源策略

瀏覽器的職責是展示/渲染document、css、script腳本等,但是這些資源(將document、css、script統一稱為資源)可能來自不同的地方,如本地、遠程服務器、甚至黑客的服務器......瀏覽器作為萬維網的入口,是我們接入互聯網最重要的軟件之一(甚至沒有之一),因此它的安全性顯得尤為重要,這就出現了瀏覽器的同源策略。

同源策略是瀏覽器一個重要的安全策略,它用于限制一個origin源的document或者它加載的腳本如何能與另一個origin源的資源進行交互。它能幫助阻隔惡意文檔,減少(并不是杜絕)可能被攻擊的媒介。

❝方便和安全往往是相悖的:安全性增高了,方便性就會有所降低❞那么問題來了,什么才算同源?

同源的定義

URL被稱作:統一資源定位符,同源是針對URL而言的。一個完整的URL各部分如下圖所示:

❝Tips:域名和host是等同的概念,域名+端口號 = host+端口號(大部分情況下你看到域名并沒有端口號,那是采用了默認端口號80而已)❞同源:只和上圖的前兩部分(protocol + domain)有關,規則為:全部相同則為同源。這個定義不難理解,但有幾點需要再強調一下:

  • 兩部分必須完全一樣才算同源
  • 這里的domain包含port端口號,所以總共是兩部分而非三部分

當然也有說三部分的(協議+host+port),理解其含義就成

下面通過舉例來徹底了解下。譬如,我的源URL為:http://www.baidu.com/api/user,下面表格描述了不同URL的各類情況:

不同源的網絡訪問

瀏覽器同源策略的存在,限制了不同源之間的交互,實為不便。但是瀏覽器也開了一些“綠燈”,讓其不受同源策略的約束。此種情況一般可分為如下三類:

1.跨域寫操作(Cross-origin writes):一般是被允許的。如鏈接(如a標簽)、重定向以及表單提交(如form表單的提交)

2.跨域資源嵌入(Cross-origin embedding):一般是允許的。比如下面這些例子:

  • <script src="..."></script>標簽嵌入js腳本
  • <link rel="stylesheet" href="...">標簽嵌入CSS
  • <img>展示的圖片
  • <video>和<audio>媒體資源
  • <object>、 <embed> 、<applet>嵌入的插件
  • CSS中使用@font-face引入字體
  • 通過<iframe>載入資源

3.跨域讀操作(Cross-origin reads):一般是不被允許的。比如我們的http接口請求等都屬于此范疇,也是本專欄關注的焦點

簡單總結成一句話:瀏覽器自己是可以發起跨域請求的(比如a標簽、img標簽、form表單等),但是Javascript是不能去跨域獲取資源(如ajax)。

如何允許不同源的網絡訪問

上面說到的第三種情況:跨域讀操作一般是不允許跨域訪問的,而這種情況是我們開發過程中最關心、最常見的case,因此必須解決。

❝Tips:這里的讀指的是廣義上的讀,指的是從服務器獲取資源(有response)的都叫讀操作,而和具體是什么Http Method無關。換句話講,所有的Http API接口請求都在這里都指的是讀操作❞可以使用 CORS 來允許跨源訪問。CORS 是 HTTP 的一部分,它允許服務端來指定哪些主機可以從這個服務端加載資源。

什么是Cors跨域

Cors(Cross-origin resource sharing):跨域資源共享,它是瀏覽器的一個技術規范,由W3C規定,規范的wiki地址在此:https://www.w3.org/wiki/CORS_Enabled#What_is_CORS_about.3F

❝話外音:它是瀏覽器的一種(自我保護)行為,并且已形成規范。也就是說:backend請求backend是不存在此現象的嘍❞若想實現Cors機制的跨域請求,是需要瀏覽器和服務器同時支持的。關于瀏覽器對CORS的支持情況:現在都2021年了,so可以認為100%的瀏覽器都是支持的,再加上CORS的整個過程都由瀏覽器自動完成,前端無需做任何設置,所以前端工程師的ajax原來怎么用現在還是怎么用,它對前段開發人員是完全透明的。

為何需要Cors跨域訪問?

瀏覽器費盡心思的搞個同源策略來保護我們的安全,但為何又需要跨域來打破這種安全策略呢?其實啊,這一切都和互聯網的快速發展有關~

隨著Web開放的程度越來越高,頁面的內容也是越來越豐富。因此頁面上出現的元素也就越來越多:圖片、視頻、各種文字內容等。為了分而治之,一個頁面的內容可能來自不同地方,也就是不同的domain域,因此通過API跨域訪問成了必然。

瀏覽器作為進入Internet最大的入口,很長時間它是個大互聯公司的必爭之地,因此市面上并存的瀏覽器種類繁多且魚龍混扎:IE 7、8、9、10,Chrome、Safari、火狐,每個瀏覽器對跨域的實現可能都不一樣。因此對開發者而言亟待需要一個規范的、統一方案,它就是Cors。

CORS(Cross-Origin Resource Sharing)由W3C組織于2009-03-17編寫工作草案,直到2014-01-16才正式畢業成為行業規范,所有瀏覽器得以遵守。至此,程序員同學們在解決跨域問題上,只需按照Cors規范實施即可。

Cors的工作原理

Web資源涉及到兩個角色:瀏覽器(消費者)和服務器(提供者),面向這兩個角色來了解Cors的原理非常簡單,如下圖所示:

1.若瀏覽器發送的是個跨域請求,http請求中就會攜帶一個名為Origin的頭表明自己的“位置”,如Origin: http://localhost:5432

2.服務端接到請求后,就可以根據傳過來的Origin頭做邏輯,決定是否要將資源共享給這個源嘍。而這個決定通過響應頭Access-Control-Allow-Origin來承載,它的value值可以是任意值,有如下情況:

  • 值為*,通配符,允許所有的Origin共享此資源
  • 值為http://localhost:5432(也就是和Origin相同),共享給此Origin
  • 值為非http://localhost:5432(也就是和Origin不相同),不共享給此Origin
  • 無此頭:不共享給此origin
  • 有此頭:值有如下可能情況

3.瀏覽器接收到Response響應后,會去提取Access-Control-Allow-Origin這個頭。然后根據上述規則來決定要接收此響應內容還是拒絕

❝Tips:Access-Control-Allow-Origin響應頭只能有1個,且value值就是個字符串。另外,value值即使寫為http://aa.com,http://bb.com這種也屬于一個而非兩個值❞

Cors細粒度控制:授權響應頭

在Cors規范中,除了可以通過Access-Control-Allow-Origin響應頭來對主體資源(URL級別)進行授權外,還提供了針對于具體響應頭更細粒度的控制,這個響應頭就是:Access-Control-Expose-Headers。換句話講,該頭用于規定哪些響應頭(們)可以暴露給前端,默認情況下這6個響應頭無需特別的顯示指定就支持:

  • Cache-Control
  • Content-Language
  • Content-Type
  • Expires
  • Last-Modified
  • Pragma

若不在此值里面的頭將不會返回給前端(其實返回了,只是瀏覽器讓其對前端不可見了而已,對JavaScript也不可見哦)。

但是,但是,但是,這種細粒度控制header的機制對簡單請求是無效的,只針對于非簡單請求(也叫復雜請求)。由此可見,將哪些類型的跨域資源請求劃分為簡單請求的范疇就顯得特備重要了。

何為簡單請求

Cors規范定義簡單請求的原則是:請求不是以更新(添加、修改和刪除)資源為目的,服務端對請求的處理不會導致自身維護資源的改變。對于簡單跨域資源請求來說,瀏覽器將兩個步驟(取得授權和獲取資源)合二為一,由于不涉及到資源的改變,所以不會帶來任何副作用。

對于一個請求,必須同時符合如下要求才被劃為簡單請求:

1.Http Method只能為其一:

  • GET
  • POST
  • HEAD

2.請求頭只能在如下范圍:

  • application/x-www-form-urlencoded
  • multipart/form-data
  • text/plain
  • Accept
  • Accept-Language
  • Content-Language
  • Content-Type,其中它的值必須如下其一:

除此之外的請求都為非簡單請求(也可稱為復雜請求)。非簡單請求可能對服務端資源改變,因此Cors規定瀏覽器在發出此類請求之前必須有一個“預檢(Preflight)”機制,這也就是我們熟悉的OPTIONS請求。

什么是Preflight預檢機制

顧名思義,它表示在瀏覽器發出真正請求之前,先發送一個預檢請求,這個在Http里就是OPTIONS請求方式。這個請求很特殊,它不包含主體(無請求參數、請求體等),主要就是將一些憑證、授權相關的輔助信息放在請求頭里交給服務器去做決策。因此它除了攜帶Origin請求頭外,還會額外攜帶如下兩個請求頭:

  • Access-Control-Request-Method:真正請求的方法
  • Access-Control-Request-Headers:真正請求的自定義請求頭(若沒有自定義的就是空唄)

服務端在接收到此類請求后,就可以根據其值做邏輯決策啦。如果允許預檢請求通過,返回個200即可,否則返回400或者403唄。

如果預檢成功,在響應里應該包含上文提到的響應頭Access-Control-Allow-Origin和Access-Control-Expose-Headers,除此之外,服務端還可以做更精細化的控制,這些精細化控制的響應頭為:

  • Access-Control-Allow-Methods:允許實際請求的Http方法(們)
  • Access-Control-Allow-Headers:允許實際請求的請求頭(們)
  • Access-Control-Max-Age:允許瀏覽器緩存此結果多久,單位:秒。有了緩存,以后就不用每次請求都發送預檢請求啦

❝說明:以上響應頭并不是必須的。若沒有此響應頭,代表接受所有❞

預檢請求完成后,有個關鍵點,便是瀏覽器拿到預檢請求的響應后的處理邏輯,這里描述如下:

1.先通過自己的Origin匹配預檢響應中的Access-Control-Allow-Origin的值,若不匹配就結束請求,若匹配就繼續下一步驗證

  • 關于Access-Control-Allow-Origin的驗證邏輯,請參考文上描述

2.拿到預檢響應中的Access-Control-Allow-Methods頭。若此頭不存在,則進行下一步,若存在則校驗預檢請求頭Access-Control-Request-Method的值是否在此列表中,在其內繼續下一步,否則失敗

3.拿到預檢響應中的Access-Control-Request-Headers頭。同請求頭中的Access-Control-Allow-Headers值記性比較,全部包含在內則匹配成功,否則失敗

以上全部匹配成功,就代表預檢成功,可以開始發送正式請求了。值得一提的事,Access-Control-Max-Age控制預檢結果的瀏覽器緩存,若緩存還生效的話,是不用單獨再發送OPTIONS請求的,匹配成功直接發送目標真實即可。

Access-Control-Max-Age使用細節

Access-Control-Max-Age用于控制瀏覽器緩存預檢請求結果的時間,這里存在一些使用細節你需要注意:

1.若瀏覽器禁用了緩存,也就是勾選了Disable cache,那么此屬性無效。也就說每次都還得發送OPTIONS請求

2.判斷此緩存結果的因素有兩個:

  • 必須是同一URL(也就是Origin相同才會去找對應的緩存)
  • header變化了,也會重新去發OPTIONS請求(當然若去掉一些header編程簡單請求了,就另當別論嘍)

跨域請求代碼示例

正所謂說再多,也抵不上跑幾個case,畢竟show me your code才是最重要。下面就針對跨域情況的簡單請求、非簡單請求(預檢通過、預檢不通過)等case分別用代碼(基于文首代碼)說明。

簡單請求

簡單請求正如其名,是最簡單的請求方式。

  1. // 跨域請求 
  2. $.get("http://localhost:8080/cors"function (result) { 
  3.     $("#content").append(result).append("<br/>"); 
  4. }); 

服務端結果:

  1. INFO ...CorsServlet - 收到請求:/cors,方法:GET, Origin頭:http://localhost:63342 

瀏覽器結果:

若想讓請求正常,只需在服務端響應頭里“加點料”就成:

  1. ... 
  2. resp.setHeader("Access-Control-Allow-Origin","http://localhost:63342"); 
  3. resp.getWriter().write("hello cors..."); 
  4. ... 

再次請求,結果成功:

對于簡單請求來講,服務端只需要設置Access-Control-Allow-Origin這個一個頭即可,一個即可。

非簡單請求

非簡單請求的模擬非常簡單,隨便打破一個簡單請求的約束即可。比如我們先在上面get請求的基礎上自定義個請求頭:

  1. $.ajax({ 
  2.     type: "get"
  3.     url: "http://localhost:8080/cors"
  4.     headers: {secret:"kkjtjnbgjlfrfgv",token: "abc123"
  5. }); 

服務端代碼:

  1. /** 
  2.  * 在此處添加備注信息 
  3.  * 
  4.  * @author YourBatman. <a href=mailto:yourbatman@aliyun.com>Send email to me</a> 
  5.  * @site https://yourbatman.cn 
  6.  * @date 2021/6/9 10:36 
  7.  * @since 0.0.1 
  8.  */ 
  9. @Slf4j 
  10. @WebServlet(urlPatterns = "/cors"
  11. public class CorsServlet extends HttpServlet { 
  12.  
  13.     @Override 
  14.     protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { 
  15.         String requestURI = req.getRequestURI(); 
  16.         String method = req.getMethod(); 
  17.         String originHeader = req.getHeader("Origin"); 
  18.  
  19.         log.info("收到請求:{},方法:{}, Origin頭:{}", requestURI, method, originHeader); 
  20.  
  21.         resp.setHeader("Access-Control-Allow-Origin","http://localhost:63342"); 
  22.         resp.setHeader("Access-Control-Expose-Headers","token,secret"); 
  23.         resp.setHeader("Access-Control-Allow-Headers","token,secret"); // 一般來講,讓此頭的值是上面那個的【子集】(或相同) 
  24.         resp.getWriter().write("hello cors..."); 
  25.     } 

點擊按鈕,瀏覽器發送請求,結果為:

服務端沒有任何日志輸出,也就是說瀏覽器并未把實際請求發出去。什么原因?查看OPTIONS請求的返回一看便知:

根本原因為:OPTIONS的響應頭里并未含有任何跨域相關信息,雖然預檢通過(注意:這個預檢是通過的喲,預檢不通過的場景就不用額外演示了吧~),但預檢的結果經瀏覽器判斷此跨域實際請求不能發出,所以給攔下來了。

從代碼層面問題就出現在resp.setHeader(xxx,xxx)放在了處理實際方法的Get方法上,顯然不對嘛,應該放在doOptions()方法里才行:

  1. @Override 
  2. protected void doOptions(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { 
  3.     super.doOptions(req, resp); 
  4.     resp.setHeader("Access-Control-Allow-Origin","http://localhost:63342"); 
  5.     resp.setHeader("Access-Control-Expose-Headers","token,secret"); 
  6.     resp.setHeader("Access-Control-Allow-Headers","token,secret"); // 一般來講,讓此頭的值是上面那個的【子集】(或相同) 

在此運行,一切正常:

值得特別注意的是:設置跨域的響應頭這塊代碼,在處理真實請求的doGet里也必須得有,否則服務端處理了,瀏覽器“不認”也是會出跨域錯誤的。

另外就是,Access-Control-Allow-Headers/Access-Control-Expose-Headers這兩個頭里必須包含你的請求的自定義的Header(標準的header不需要包含),否則依舊跨域失敗哦~

在實際生產場景中,Http請求的Content-type大都是application/json并非簡單請求的頭,所以有個現實情況是:實際的跨域請求中,幾乎100%的情況下我們發的都是非簡單請求。

Cros跨域使用展望

如上代碼示例,處理簡單請求尚且簡單,但對于非簡單請求來說,我們在doOptions和doGet都寫了一段setHeader的代碼,是否覺得麻煩呢?

另外,對于Access-Control-Allow-Origin若我需要允許多個源怎么辦呢?

❝Tips:Access-Control-Allow-Origin頭只允許一個,且Access-Control-Allow-Origin:a.com,b.com依舊算作一個源的,它沒有逗號分隔的“特性”。從命名的藝術你也可看出,它并非是xxx-Origins而是xxx-Origin❞既然實際場景中幾乎100%都是非簡單請求,那么對于控制非簡單請求的Access-Control-Allow-Methods、Access-Control-Allow-Headers、Access-Control-Max-Age這些都都改如何賦值?是否有最佳實踐?

現在我們大都在Spring Framework/Spring Boot場景下開發應用,框架層面是否提供一些優雅的解決方案?

作為一名后端開發工程師(編程語言不限),也許你從未處理過跨域問題,那么到底是誰默默的幫你解決了這一切呢?是否想知其所以然?

總結

本文用很長的篇幅介紹了Cors跨域資源共享的相關知識,并且用代碼做了示范,希望能助你通關Cors這個狗皮膏藥一樣粘著我們的硬核知識點。本文文字敘述較多,介紹了同源、跨域、Cors的幾乎所有概念,雖然略顯難啃,但這些是指導我們實踐的說明書。

本文轉載自微信公眾號「 BAT的烏托邦」,可以通過以下二維碼關注。轉載本文請聯系 BAT的烏托邦公眾號。

 

責任編輯:姜華 來源: BAT的烏托邦
相關推薦

2019-04-10 10:32:16

CORSNginx反向代理

2022-04-29 09:11:14

CORS瀏覽器

2021-06-15 07:32:59

Cookie和Sess實現跨域

2014-08-19 10:36:02

AngularCORS

2011-01-24 13:12:01

AjaxDojojavascript

2019-03-13 08:00:00

JavaScript作用域前端

2021-03-09 08:35:51

JSS作用域前端

2021-06-25 09:04:39

Cors跨域JSONP vs CO

2011-09-06 09:56:24

JavaScript

2023-12-20 14:42:59

2024-01-31 07:55:52

2017-05-25 09:45:35

2020-08-31 19:20:33

瀏覽器CORS跨域

2013-11-27 10:23:23

2016-11-01 21:51:03

phpjavascript

2023-11-20 08:02:49

2018-12-12 15:50:13

2024-08-28 08:45:22

2019-03-13 14:15:25

CORS跨域資源前端

2021-06-17 07:15:36

Cors跨域多域名
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 亚洲视频www| 午夜欧美 | 亚洲区在线 | 国产精品久久久久999 | 青青草社区 | av一区二区三区四区 | 91精品国产一区二区三区 | 亚洲国产精品久久久久秋霞不卡 | 全免费a级毛片免费看视频免 | 一区亚洲| av网站免费 | 亚洲成人国产 | 国产精品色 | 欧美在线视频a | 夜夜草av | 国产精品视频一区二区三区不卡 | 99亚洲精品| 久久亚洲一区 | 四虎影音 | 国产三区在线观看视频 | 中文精品久久 | 欧美成人激情视频 | 国产精品久久久久久久久久久久久 | 欧美二区在线 | 精品久久久久久久久久 | 精区3d动漫一品二品精区 | 国产美女黄色片 | 毛片毛片毛片毛片毛片 | 在线亚洲欧美 | av三级| 成人国产精品久久 | 亚洲一二视频 | 中文字幕在线网 | 日日夜夜天天干 | 亚洲精品www | 国产黄色av网站 | 日韩二 | 欧美一级黄色免费看 | 国产91av视频| 成人av一区二区在线观看 | 欧美9999|