精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

在WebView中如何讓JS與Java安全地互相調用

移動開發 Android
在現在安卓應用原生開發中,為了追求開發的效率以及移植的便利性,使用WebView作為業務內容展示與交互的主要載體是個不錯的折中方案。那么在 這種Hybrid(混合式) App中,難免就會遇到頁面JS需要與Java相互調用,調用Java方法去做那部分網頁JS不能完成的功能。

[[120265]]

在現在安卓應用原生開發中,為了追求開發的效率以及移植的便利性,使用WebView作為業務內容展示與交互的主要載體是個不錯的折中方案。那么在 這種Hybrid(混合式) App中,難免就會遇到頁面JS需要與Java相互調用,調用Java方法去做那部分網頁JS不能完成的功能。

網上的方法可以告訴我們這個時候我們可以使用addjavascriptInterface來注入原生接口到 JS中,但是在安卓4.2以下的系統中,這種方案卻我們的應用帶來了很大的安全風險。攻擊者如果在頁面執行一些非法的JS(誘導用戶打開一些釣魚網站以進 入風險頁面),極有可能反彈拿到用戶手機的shell權限。接下來攻擊者就可以在后臺默默安裝木馬,完全洞穿用戶的手機。詳細的攻擊過程可以見烏云平臺的 這份報告:WebView中接口隱患與手機掛馬利用。

安卓4.2及以上版本(API >= 17),在注入類中為可調用的方法添加@JavascriptInterface注解,無注解的方法不能被調用,這種方式可以防范注入漏洞。那么有沒一種 安全的方式,可以完全兼顧安卓4.2以下版本呢?答案就是使用prompt,即WebChromeClient 輸入框彈出模式。

我們參照 Android WebView的Js對象注入漏洞解決方案 這篇文章給出的解決方案, 但它JS下的方法有點笨拙, 動態生成JS文件過程也并沒有清晰,且加載JS文件的時機也沒有準確把握。那么如何改造才能便利地在JS代碼中調用Java方法,并且安全可靠呢?

下面提到的源碼及項目可以在這找到Safe Java-JS Bridge In Android WebView[Github]

一、動態地生成將注入的JS代碼

JsCallJava在構造時,將要注入類的public且static方法拿出來,逐個生成方法的簽名,依據方法簽名先將方法緩存起來,同時結合方法名稱與靜態的HostApp-JS代碼動態生成一段將要注入到webview中的字符串。

  1. public JsCallJava (String injectedName, Class injectedCls) { 
  2.     try { 
  3.         mMethodsMap = new HashMap<String, Method>(); 
  4.         //獲取自身聲明的所有方法(包括public private protected), getMethods會獲得所有繼承與非繼承的方法 
  5.         Method[] methods = injectedCls.getDeclaredMethods(); 
  6.         StringBuilder sb = new StringBuilder("javascript:(function(b){console.log(\"HostApp initialization begin\");var a={queue:[],callback:function(){var d=Array.prototype.slice.call(arguments,0);var c=d.shift();var e=d.shift();this.queue[c].apply(this,d);if(!e){delete this.queue[c]}}};"); 
  7.  
  8.         for (Method method : methods) { 
  9.             String sign; 
  10.             if (method.getModifiers() != (Modifier.PUBLIC | Modifier.STATIC) || (sign = genJavaMethodSign(method)) == null) { 
  11.                 continue
  12.             } 
  13.             mMethodsMap.put(sign, method); 
  14.             sb.append(String.format("a.%s=", method.getName())); 
  15.         } 
  16.  
  17.         sb.append("function(){var f=Array.prototype.slice.call(arguments,0);if(f.length<1){throw\"HostApp call error, message:miss method name\"}var e=[];for(var h=1;h<f.length;h++){var c=f[h];var j=typeof c;e[e.length]=j;if(j==\"function\"){var d=a.queue.length;a.queue[d]=c;f[h]=d}}var g=JSON.parse(prompt(JSON.stringify({method:f.shift(),types:e,args:f})));if(g.code!=200){throw\"HostApp call error, code:\"+g.code+\", message:\"+g.result}return g.result};Object.getOwnPropertyNames(a).forEach(function(d){var c=a[d];if(typeof c===\"function\"&&d!==\"callback\"){a[d]=function(){return c.apply(a,[d].concat(Array.prototype.slice.call(arguments,0)))}}});b." + injectedName + "=a;console.log(\"HostApp initialization end\")})(window);"); 
  18.         mPreloadInterfaceJS = sb.toString(); 
  19.     } catch(Exception e){ 
  20.         Log.e(TAG, "init js error:" + e.getMessage()); 
  21.     } 
  22.  
  23. private String genJavaMethodSign (Method method) { 
  24.     String sign = method.getName(); 
  25.     Class[] argsTypes = method.getParameterTypes(); 
  26.     int len = argsTypes.length; 
  27.     if (len < 1 || argsTypes[0] != WebView.class) { 
  28.         Log.w(TAG, "method(" + sign + ") must use webview to be first parameter, will be pass"); 
  29.         return null
  30.     } 
  31.     for (int k = 1; k < len; k++) { 
  32.         Class cls = argsTypes[k]; 
  33.         if (cls == String.class) { 
  34.             sign += "_S"
  35.         } else if (cls == int.class || 
  36.             cls == long.class || 
  37.             cls == float.class || 
  38.             cls == double.class) { 
  39.             sign += "_N"
  40.         } else if (cls == boolean.class) { 
  41.             sign += "_B"
  42.         } else if (cls == JSONObject.class) { 
  43.             sign += "_O"
  44.         } else if (cls == JsCallback.class) { 
  45.             sign += "_F"
  46.         } else { 
  47.             sign += "_P"
  48.         } 
  49.     } 
  50.     return sign; 

從上面可以看出,類的各個方法名稱被拼接到前后兩段靜態壓縮的JS代碼當中,那么這樣生成的完整清晰的HostApp-JS片段是怎樣的呢? 我們假設HostJsScope類中目前只定義了toast、alert、getIMSI這三個公開靜態方法,那么完整的片段就是下面這樣:

  1. (function(global){ 
  2.     console.log("HostApp initialization begin"); 
  3.     var hostApp = { 
  4.         queue: [], 
  5.         callback: function () { 
  6.             var args = Array.prototype.slice.call(arguments, 0); 
  7.             var index = args.shift(); 
  8.             var isPermanent = args.shift(); 
  9.             this.queue[index].apply(this, args); 
  10.             if (!isPermanent) { 
  11.                 delete this.queue[index]; 
  12.             } 
  13.         } 
  14.     }; 
  15.     hostApp.toast = hostApp.alert = hostApp.getIMSI = function () { 
  16.         var args = Array.prototype.slice.call(arguments, 0); 
  17.         if (args.length < 1) { 
  18.             throw "HostApp call error, message:miss method name"
  19.         } 
  20.         var aTypes = []; 
  21.         for (var i = 1;i < args.length;i++) { 
  22.             var arg = args[i]; 
  23.             var type = typeof arg; 
  24.             aTypes[aTypes.length] = type; 
  25.             if (type == "function") { 
  26.                 var index = hostApp.queue.length; 
  27.                 hostApp.queue[index] = arg; 
  28.                 args[i] = index; 
  29.             } 
  30.         } 
  31.         var res = JSON.parse(prompt(JSON.stringify({ 
  32.             method: args.shift(), 
  33.             types: aTypes, 
  34.             args: args 
  35.         }))); 
  36.  
  37.         if (res.code != 200) { 
  38.             throw "HostApp call error, code:" + res.code + ", message:" + res.result; 
  39.         } 
  40.         return res.result; 
  41.     }; 
  42.  
  43.     //有時候,我們希望在該方法執行前插入一些其他的行為用來檢查當前狀態或是監測 
  44.     //代碼行為,這就要用到攔截(Interception)或者叫注入(Injection)技術了 
  45.     /** 
  46.      * Object.getOwnPropertyName 返回一個數組,內容是指定對象的所有屬性 
  47.      * 
  48.      * 其后遍歷這個數組,分別做以下處理: 
  49.      * 1. 備份原始屬性; 
  50.      * 2. 檢查屬性是否為 function(即方法); 
  51.      * 3. 若是重新定義該方法,做你需要做的事情,之后 apply 原來的方法體。 
  52.      */ 
  53.     Object.getOwnPropertyNames(hostApp).forEach(function (property) { 
  54.         var original = hostApp[property]; 
  55.  
  56.         if (typeof original === 'function'&&property!=="callback") { 
  57.             hostApp[property] = function () { 
  58.                 return original.apply(hostApp,  [property].concat(Array.prototype.slice.call(arguments, 0))); 
  59.             }; 
  60.         } 
  61.     }); 
  62.     global.HostApp = hostApp; 
  63.     console.log("HostApp initialization end"); 
  64. })(window); 

其實在JsCallJava初始化時我們拼接的只是上面第15行 hostApp.toast = hostApp.alert = hostApp.getIMSI = function () 這段。目的是將所有JS層調用函數嫁接到一個匿名函數1中,而后利用攔截技術,遍歷hostApp下所有的函數,拿出對應的函數名,然后將hostApp 下所有的函數調用嫁接到另一個匿名函數2,這樣做的目的是hostApp下函數調用時首先執行匿名函數2,匿名函數2將對應的函數名作為第一個參數然后再 調用匿名函數1,這樣匿名函數1中就能區分執行時調用來源。實現了JS層調用入口統一,返回出口統一的結構體系。

二、HostApp JS片段注入時機

步驟一說明了HostApp-JS片段的拼接方法,同時JS片段拼接是在JsCallJava初始化完成的,而JsCallJava初始化是在實例化InjectedChromeClient對象時發起的。

  1. public InjectedChromeClient (String injectedName, Class injectedCls) { 
  2.     mJsCallJava = new JsCallJava(injectedName, injectedCls); 

從步驟一的代碼,我們知道JsCallJava拼接出來的JS代碼暫時被存到mPreloadInterfaceJS字段中。那么我們何時把這段代碼串注入到Webview的頁面空間內呢?答案是頁面加載進度變化的過程中。

  1. @Override 
  2. public void onProgressChanged (WebView view, int newProgress) { 
  3.     //為什么要在這里注入JS 
  4.     //1 OnPageStarted中注入有可能全局注入不成功,導致頁面腳本上所有接口任何時候都不可用 
  5.     //2 OnPageFinished中注入,雖然最后都會全局注入成功,但是完成時間有可能太晚,當頁面在初始化調用接口函數時會等待時間過長 
  6.     //3 在進度變化時注入,剛好可以在上面兩個問題中得到一個折中處理 
  7.     //為什么是進度大于25%才進行注入,因為從測試看來只有進度大于這個數字頁面才真正得到框架刷新加載,保證100%注入成功 
  8.     if (newProgress <= 25) { 
  9.         mIsInjectedJS = false
  10.     } else if (!mIsInjectedJS) { 
  11.         view.loadUrl(mJsCallJava.getPreloadInterfaceJS()); 
  12.         mIsInjectedJS = true
  13.         Log.d(TAG, " inject js interface completely on progress " + newProgress); 
  14.  
  15.     } 
  16.     super.onProgressChanged(view, newProgress); 

從上面我們可以看出,注入的時機是準確把握在進度大于25%時。如果在OnPageFinished注入,頁面document.ready的初始回調會等待時間過長,詳細的原因我們會在后面講到。

三、頁面調用Java方法執行的過程

OK,上面兩步解決了動態生成與成功注入的兩大問題,接下來就要處理JS具體的調用過程。上面,我們知道頁面調用Java方法時,匿名js函數在拼 接好參數后prompt json數據。prompt消息被Java層的WebChromeClient.onJsPrompt攔截到。

  1. @Override 
  2. public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result) { 
  3.     result.confirm(mJsCallJava.call(view, message)); 
  4.     return true

而JsCallJava.call的具體實現如下。

  1. public String call(WebView webView, String jsonStr) { 
  2.     if (!TextUtils.isEmpty(jsonStr)) { 
  3.         try { 
  4.             JSONObject callJson = new JSONObject(jsonStr); 
  5.             String methodName = callJson.getString("method"); 
  6.             JSONArray argsTypes = callJson.getJSONArray("types"); 
  7.             JSONArray argsVals = callJson.getJSONArray("args"); 
  8.             String sign = methodName; 
  9.             int len = argsTypes.length(); 
  10.             Object[] values = new Object[len + 1]; 
  11.             int numIndex = 0
  12.             String currType; 
  13.  
  14.             values[0] = webView; 
  15.  
  16.             for (int k = 0; k < len; k++) { 
  17.                 currType = argsTypes.optString(k); 
  18.                 if ("string".equals(currType)) { 
  19.                     sign += "_S"
  20.                     values[k + 1] = argsVals.isNull(k) ? null : argsVals.getString(k); 
  21.                 } else if ("number".equals(currType)) { 
  22.                     sign += "_N"
  23.                     numIndex = numIndex * 10 + k + 1
  24.                 } else if ("boolean".equals(currType)) { 
  25.                     sign += "_B"
  26.                     values[k + 1] = argsVals.getBoolean(k); 
  27.                 } else if ("object".equals(currType)) { 
  28.                     sign += "_O"
  29.                     values[k + 1] = argsVals.isNull(k) ? null : argsVals.getJSONObject(k); 
  30.                 } else if ("function".equals(currType)) { 
  31.                     sign += "_F"
  32.                     values[k + 1] = new JsCallback(webView, argsVals.getInt(k)); 
  33.                 } else { 
  34.                     sign += "_P"
  35.                 } 
  36.             } 
  37.  
  38.             Method currMethod = mMethodsMap.get(sign); 
  39.  
  40.             // 方法匹配失敗 
  41.             if (currMethod == null) { 
  42.                 return getReturn(jsonStr, 500"not found method(" + methodName + ") with valid parameters"); 
  43.             } 
  44.             // 數字類型細分匹配 
  45.             if (numIndex > 0) { 
  46.                 Class[] methodTypes = currMethod.getParameterTypes(); 
  47.                 int currIndex; 
  48.                 Class currCls; 
  49.                 while (numIndex > 0) { 
  50.                     currIndex = numIndex - numIndex / 10 * 10
  51.                     currCls = methodTypes[currIndex]; 
  52.                     if (currCls == int.class) { 
  53.                         values[currIndex] = argsVals.getInt(currIndex - 1); 
  54.                     } else if (currCls == long.class) { 
  55.                         //WARN: argsJson.getLong(k + defValue) will return a bigger incorrect number 
  56.                         values[currIndex] = Long.parseLong(argsVals.getString(currIndex - 1)); 
  57.                     } else { 
  58.                         values[currIndex] = argsVals.getDouble(currIndex - 1); 
  59.                     } 
  60.                     numIndex /= 10
  61.                 } 
  62.             } 
  63.  
  64.             return getReturn(jsonStr, 200, currMethod.invoke(null, values)); 
  65.         } catch (Exception e) { 
  66.             //優先返回詳細的錯誤信息 
  67.             if (e.getCause() != null) { 
  68.                 return getReturn(jsonStr, 500"method execute error:" + e.getCause().getMessage()); 
  69.             } 
  70.             return getReturn(jsonStr, 500"method execute error:" + e.getMessage()); 
  71.         } 
  72.     } else { 
  73.         return getReturn(jsonStr, 500"call data empty"); 
  74.     } 

這是一個完整的解析匹配過程,會依據js層傳入的方法名、參數類型列表再次生成方法簽名,與之前初始化構造好的緩存對象中的方法匹配。匹配成功后則 判斷js調用參數類型中是否有number類型,如果有依據Java層方法的定義決定是取int、long還是double類型的值。最后使用調用值列表 和方法對象反射執行,返回函數執行的結果。這里有幾點需要注意:

  • 方法反射執行時會將當前WebView的實例放到第一個參數,方便在HostJsScope靜態方法依據Context拿到一些相關上下文信息;
  • 注入類(如HostJsScope)靜態方法的參數定義可使用的類型有int/long/double、String、boolean、 JSONObject、JsCallback,對應于js層傳入的類型為number、string、boolean、object、function, 注意number數字過大時(如時間戳),可能需要先轉為string類型(Java方法中參數也須定義為String),避免精度丟失;
  • Java方法的返回值可以是void 或 能轉為字符串的類型(如int、long、String、double、float等)或 可序列化的自定義類型;
  • 如果執行失敗或找不到調用方法時,Java層會將異常信息傳遞到JS層, JS匿名函數中會throw拋出錯誤;

四、HostApp在頁面的使用

有了上面的準備工作,現在我們在頁面中就可以很方便地使用HostApp了,而不需要加載任何依賴文件。如li標簽的點擊:

  1. <ul class="entry"> 
  2.     <li onclick="HostApp.alert('HostApp.alert');">HostApp.alert</li> 
  3.     <li onclick="HostApp.toast('HostApp.toast');">HostApp.toast</li> 
  4.     <li onclick="HostApp.testLossTime(new Date().getTime() + '');">HostApp.testLossTime</li> <!-- 時間戳長整型調用前先轉換為string --> 
  5.     <li onclick="HostApp.toast(HostApp.getIMSI());">HostApp.getIMSI</li> 
  6. </ul> 

但同時有一種業務情景時,頁面初始加載完備時就應立即觸發的調用,如果我們這樣寫:

  1. document.addEventListener('DOMContentLoaded', function() { 
  2.     HostApp.toast('document ready now');; 
  3. }, false); 

那么HostApp的調用極有可能不成功,因為端注入HostApp-JS片段的時機可能在document.ready前也可能在其后。那么如何解決這個矛盾的問題呢?

如果document.ready的時候HostApp JS已經注入成功,這種情況OK沒有問題。當document.ready的時候HostApp JS還未開始注入,這種情景下我們的js腳本層就需要做出變動,即輪詢狀態,直到端注入成功或者超時(1.5s),再發生回調。具體實現如下(下面的是以 zepto.js的$.ready()函數改造為例)。

  1. //針對DOM的一些操作 
  2. // Define methods that will be available on all 
  3. // Zepto collections 
  4. $.fn = { 
  5.     //DOM Ready 
  6.     ready: function(callback, jumpHostAppInject) { 
  7.         var originCb = callback; 
  8.         var mcounter = 0
  9.         //嘗試等待(1500ms超時)讓端注入HostApp Js 
  10.         callback = function () { 
  11.             if(!window.HostApp && mcounter++ < 150)setTimeout(callback, 10);else originCb($); 
  12.         }; 
  13.         //是否跳過等待HostApp的注入 
  14.         if (jumpHostAppInject) { 
  15.             callback = originCb; 
  16.         } 
  17.         if (readyRE.test(document.readyState)) callback($); else document.addEventListener('DOMContentLoaded', function() { 
  18.                 callback($) 
  19.             }, false); 
  20.         return this 
  21.     }, 
  22.     ... 
  23.     ... 
  24. }; 

這樣的機制也就解釋了為什么不把Java層的JS注入放在OnPageFinish了,如果那樣頁面輪詢的次數就會上升,等待的時間就會變長,而且有可能會超時。好了,有了上面的改動,頁面初始加載完備時需要立即觸發HostApp的調用,如下:

  1. <script type="text/javascript"> 
  2.     $(function () { 
  3.         HostApp.alert("HostApp ready now"); 
  4.     }); 
  5. </script> 

更多使用說明及完整源代碼見:Safe Java-JS Bridge In Android WebView[Github]

原文:http://www.pedant.cn/2014/07/04/webview-js-java-interface-research/

責任編輯:閆佳明 來源: pedant.cn
相關推薦

2013-12-30 10:43:15

云計算移動數據云安全

2016-05-11 14:16:20

2018-03-21 07:08:40

2022-10-12 15:15:56

數字孿生物聯網

2023-10-11 17:38:43

Linux磁盤數據

2014-06-06 14:33:29

BYOD移動安全

2022-07-04 10:11:33

云安全混合云云計算

2024-03-14 11:22:54

2019-04-11 08:00:00

Windows刪除文件

2020-03-17 16:15:01

Python編譯代碼

2024-01-10 17:27:00

Python開發

2021-09-14 09:00:00

私有云安全云架構

2019-06-17 08:00:55

multipassbash腳本

2019-12-13 11:30:33

云計算IT安全

2024-06-17 15:29:56

2023-04-01 10:32:36

2024-01-22 16:38:00

AI人工智能GenAI

2022-12-25 10:09:44

2013-08-26 09:18:21

2012-06-13 16:52:25

F5BYOD
點贊
收藏

51CTO技術棧公眾號

日韩精品导航| 三级福利片在线观看| 日韩在线一区二区三区| 国产一区二区成人| 亚洲一二三av| free性欧美16hd| 国产亚洲精品aa| 亚洲free嫩bbb| 日韩黄色a级片| 日韩av二区| 精品国产3级a| 在线观看国产中文字幕| segui88久久综合| 国产精品乱码一区二区三区软件 | 自拍偷拍校园春色| 欧美激情1区2区| 亚洲最大中文字幕| 肉丝美足丝袜一区二区三区四| 美女100%一区| 亚洲国产另类精品专区| 五月天久久狠狠| 日本xxxxxwwwww| 狠狠色丁香婷综合久久| 国产精品www| 日韩精品国产一区二区| 综合一区二区三区| 国产午夜一区二区| 极品白嫩的小少妇| 国产精品视频一区视频二区 | 成人免费性视频| 777电影在线观看| 91一区二区在线| 不卡视频一区| 国产伦理一区二区| 麻豆极品一区二区三区| 97视频在线播放| 国产在线免费视频| 欧美在线网址| 中文字幕日韩精品在线| 成年人网站免费在线观看| 大奶在线精品| 欧美不卡一二三| 免费高清视频在线观看| 亚洲精品tv| 欧美日韩视频在线第一区| 可以免费在线看黄的网站| 国产自产自拍视频在线观看| 一区二区不卡在线视频 午夜欧美不卡在| 正在播放亚洲| 日本电影全部在线观看网站视频| 国产欧美日韩视频在线观看| 欧洲一区二区日韩在线视频观看免费 | 中文字幕第4页| 欧美五码在线| 日韩黄色在线免费观看| 亚洲天堂资源在线| 老司机成人在线| 精品亚洲国产视频| 51调教丨国产调教视频| 日韩影视高清在线观看| 日韩电影中文 亚洲精品乱码 | 老司机午夜av| 91精品国产66| 欧美日韩国产免费一区二区| 中文字幕22页| 欧美经典一区| 精品处破学生在线二十三| 中文字幕18页| 亚洲另类春色校园小说| 亚洲视频综合网| 自拍偷拍第9页| 欧美在线国产| 91sa在线看| 成人黄色三级视频| 国内成人精品2018免费看| 亚洲一区二区久久久久久| 国内老熟妇对白hdxxxx| 成人国产精品免费网站| 蜜桃999成人看片在线观看| 国产福利片在线| 亚洲人成电影网站色mp4| 国产小视频免费| 免费成人在线电影| 欧美吻胸吃奶大尺度电影| 午夜福利123| 久久男人av| 中文字幕日韩精品在线| 欧美成人aaa片一区国产精品| 亚洲理伦在线| 国产精品视频精品视频| 精品人妻一区二区三区浪潮在线| 99久久精品国产麻豆演员表| 亚洲精品美女久久7777777| 丝袜美女在线观看| 一本到不卡精品视频在线观看| 老司机午夜性大片| 牛牛精品成人免费视频| 日韩视频中文字幕| 精品无码m3u8在线观看| 亚洲天堂视频在线播放| 欧洲成人综合网| 欧美日韩中国免费专区在线看| 久久午夜夜伦鲁鲁一区二区| 欧美中文高清| 亚洲欧洲中文天堂| 少妇影院在线观看| 天堂成人国产精品一区| 成人免费视频视频在| 东热在线免费视频| 亚洲国产wwwccc36天堂| 亚洲成人福利在线| 天堂综合网久久| 欧美成人精品xxx| 色老头在线视频| 成人在线视频一区| 亚洲一区尤物| 特黄毛片在线观看| 日韩免费性生活视频播放| 97人妻人人揉人人躁人人| 亚洲福利免费| 亚洲综合中文字幕在线| av天在线观看| 日韩欧美在线视频| 欧洲熟妇的性久久久久久| 欧美韩日一区| 国产精品久久电影观看| 香蕉av一区二区三区| 亚洲男同性视频| 污污视频网站免费观看| 里番精品3d一二三区| 九九精品视频在线观看| 中文字幕一区二区三区波野结| wwww国产精品欧美| 农民人伦一区二区三区| 综合伊人久久| 久久精品视频免费播放| 在线观看中文字幕av| 久久午夜免费电影| 成人小视频在线观看免费| 国产精品国产三级在线观看| 精品国产乱码久久久久久浪潮| 粉嫩av性色av蜜臀av网站| 美女视频黄免费的久久| 五月天久久综合网| 国产69精品久久久久按摩| 一区二区欧美亚洲| 青青草视频在线观看免费| 久久久久久97三级| 成人黄色片视频| 天堂综合网久久| 日本一区二区在线免费播放| 久色视频在线| 欧美视频一区二| 亚洲天堂av中文字幕| 蜜臀久久久99精品久久久久久| 亚洲欧洲精品一区二区| 激情欧美一区二区三区黑长吊| 中文字幕亚洲无线码在线一区| 日韩国产亚洲欧美| 国产蜜臀97一区二区三区| 97公开免费视频| 日韩国产一区二区| 成人精品视频久久久久| 在线三级中文| 亚洲福利影片在线| 日韩美女黄色片| 久久一区二区三区四区| 无需播放器的av| 91精品国产麻豆国产在线观看| 91在线网站视频| 男人天堂亚洲| 日韩精品视频免费在线观看| 极品国产91在线网站| 国产精品免费aⅴ片在线观看| 国产成年人视频网站| 国精品一区二区| 久久婷婷国产综合尤物精品| 国产成人亚洲一区二区三区| 久久精品小视频| 免费av一级片| 欧美午夜精品理论片a级按摩| 国产传媒免费在线观看| 99久久婷婷国产综合精品| av网址在线观看免费| 99精品视频精品精品视频| 999热视频| 美女18一级毛片一品久道久久综合| 日韩最新中文字幕电影免费看| 亚洲免费成人网| 在线精品视频一区二区三四| 男人与禽猛交狂配| 26uuu久久综合| 亚洲色图欧美自拍| 亚洲在线网站| 99精品视频网站| 九九久久精品| 91免费在线观看网站| 欧美成人精品一区二区男人小说| 久久中文字幕在线| 偷拍自拍在线视频| 911精品国产一区二区在线| 91午夜视频在线观看| 国产精品久久久久精k8| 国产精品久久久久久久久久直播| www555久久| 色婷婷**av毛片一区| 日韩在线视频第一页| 欧美日韩国产综合视频在线观看 | 国产精品亚洲网站| heyzo在线| 精品国产一区二区三区久久久狼| 五月天婷婷视频| 日韩一区二区麻豆国产| 中文天堂在线播放| 欧美色欧美亚洲高清在线视频| 欧美爱爱免费视频| 国产清纯在线一区二区www| 国产精品果冻传媒| 卡一卡二国产精品| 激情婷婷综合网| 亚洲影音一区| 亚洲 欧美 综合 另类 中字| 亚洲国产精品久久久久蝴蝶传媒| 欧洲一区二区在线观看| 色婷婷av一区二区三区丝袜美腿| 99久久精品久久久久久ai换脸| 欧美一级做a| 国产精品大陆在线观看| 欧美成人免费电影| 2019亚洲男人天堂| 牛牛精品在线| 久久亚洲电影天堂| 无遮挡的视频在线观看| 一本大道亚洲视频| 久久经典视频| 亚洲欧美国产一区二区三区| 亚洲av成人精品一区二区三区在线播放| 日韩美一区二区三区| 国产露脸国语对白在线| 欧美精品在线观看一区二区| 伊人影院中文字幕| 欧美色综合久久| 久草热在线观看| 欧美日韩免费一区二区三区视频 | 日韩一区二区三区视频在线观看| 性色av一区二区三区四区| 91福利国产精品| 蜜臀尤物一区二区三区直播| 欧美视频在线观看免费| 日韩中文字幕在线观看视频| 色哟哟一区二区三区| 亚洲黄网在线观看| 欧美色图12p| 国产精品一区二区av白丝下载| 欧美美女一区二区在线观看| 国产又粗又大又爽视频| 欧美一区二区三区男人的天堂| 国产免费无码一区二区视频| 亚洲欧美激情在线| 久久精品黄色片| 亚洲一区在线观看视频| 日韩av一二三区| 色综合一个色综合亚洲| 中文字幕 国产| 欧美老女人第四色| 性生交大片免费看女人按摩| 亚洲黄色在线看| 欧洲亚洲在线| 少妇高潮久久久久久潘金莲| 黄色大片在线播放| 九九九久久久久久| 色一区二区三区| 国产精品亚洲自拍| 91综合久久爱com| 久久99精品久久久水蜜桃| 精品视频免费| 国产av不卡一区二区| 亚洲免费激情| 午夜免费福利在线| 国产乱淫av一区二区三区| 中文字幕第3页| 亚洲国产电影在线观看| 劲爆欧美第一页| 精品欧美aⅴ在线网站| 中文字幕一区二区免费| 日韩精品一区二区在线观看| 欧美3p视频在线观看| 久久精品最新地址| 91九色国产在线播放| 国产精品午夜视频| 日韩高清一区| 欧美视频观看一区| 黄色成人av网站| 欧美精品性生活| 成人性视频网站| 精品一区二区6| 亚洲高清视频的网址| 中文文字幕一区二区三三| 精品99久久久久久| 日本最新在线视频| 91av在线影院| 日韩视频一二区| 亚州欧美一区三区三区在线| 极品少妇一区二区三区| 孩娇小videos精品| 91免费国产在线| 中文字幕影音先锋| 欧美图片一区二区三区| 手机看片福利永久| 久久亚洲欧美日韩精品专区| 亚洲成av在线| 久久99国产精品| 欧美88av| 色网站在线视频| 国产亚洲成aⅴ人片在线观看| 国产黄色片视频| 欧美一区二区在线视频| av网站无病毒在线| 国产成人精品免费视频| 久久精品福利| 成人av在线播放观看| 激情综合五月婷婷| 这里只有久久精品| 欧美日韩激情小视频| 刘亦菲久久免费一区二区| 久热爱精品视频线路一| 97欧美成人| 日本免费高清一区二区| 国产美女一区| 手机在线看片日韩| 亚洲亚洲精品在线观看| 国产成人三级在线播放| 久久久国产在线视频| 久久影视精品| 亚洲一区二区精品在线| 日本va欧美va瓶| 妺妺窝人体色WWW精品| 欧美色视频日本高清在线观看| 午夜在线观看视频18| 97香蕉久久超级碰碰高清版 | 精品美女在线播放| 深夜国产在线播放| 99视频国产精品免费观看| 欧美国内亚洲| 国产免费a级片| 亚洲一区在线看| 手机看片一区二区三区| 午夜精品美女自拍福到在线| 开心激情综合| 青青在线视频观看| 国产校园另类小说区| 成人免费毛片男人用品| 国产亚洲一级高清| 欧美一级二级视频| 一本一道久久久a久久久精品91| 免费看日韩精品| 国产黄色小视频网站| 日韩三级视频中文字幕| 免费电影网站在线视频观看福利| 国产精品久久国产精品| 99av国产精品欲麻豆| 欧美特黄一区二区三区| 欧洲av一区二区嗯嗯嗯啊| 在线播放毛片| 国产成人成网站在线播放青青| 激情婷婷欧美| 久久精品一区二区免费播放| 在线观看日韩av先锋影音电影院| 在线观看麻豆蜜桃| 亚洲qvod图片区电影| 亚洲精品女人| 黄色片网站免费| 这里是久久伊人| 97在线视频免费观看完整版| 欧美黑人3p| 韩国午夜理伦三级不卡影院| 国产在线拍揄自揄拍无码视频| 日韩精品有码在线观看| 国产成人毛片| 97超碰在线人人| 日本一区二区免费在线 | 国产精品亚洲人在线观看| 国产精品成人免费一区二区视频| 亚洲天堂精品在线| 国产精品777777在线播放| 亚洲熟妇av日韩熟妇在线| 欧美经典一区二区| 精品国自产在线观看| 欧美亚洲另类视频| 天天射综合网视频| 91视频在线免费| 欧美日韩国产bt| www.九色在线| 爱爱爱视频网站| 91美女蜜桃在线| 精品免费久久久| 国产精欧美一区二区三区| 欧美日韩国产亚洲一区| 中文字幕网站在线观看| 精品免费视频一区二区|