日本免费精品_最新日韩一区_亚洲视频一区在线_a在线视频观看_天天射夜夜骑_粉嫩av一区二区三区_欧美中日韩免费视频_综合图区欧美_国内精品美女在线观看_午夜精品久久久久久久男人的天堂

首頁 > 編程 > HTML > 正文

如何使用localstorage代替cookie實現跨域共享數據問題

2024-08-26 00:20:36
字體:
來源:轉載
供稿:網友

一,背景

 localstorage,跨域,h5,localstorage跨域共享

因為網站系統的日益龐大,不同域名業務,甚至不同合作方網站的cookie可能或多或少需要進行共享使用,遇到這個情況的時候,大家一般想到的是使用登錄中心分發cookie狀態再進行同步進行解決,成本較高而且實施起來比較復雜和麻煩。

因為cookie在跨域的情況下,瀏覽器根本不允許互相訪問的限制,為了突破這個限制,所以有了以下這個實現方案,使用postmessage和localstorage進行數據跨域共享。

原理比較簡單,但是遇到的坑也不少,這里梳理一下,做個備份。

二,API設計

背景中說過我們使用localstorage來代替cookie,本身localstorage和cookie就有一些使用上的區別,比如localstorage的容量更大,但是不存在過期時間,雖然容量大,但在不同的瀏覽器上也都有空間上限,操作不好很容易崩潰,還有就是postmessage雖然支持跨域,安全問題和api的異步化也給使用帶來了一些麻煩,我們如何把這個模塊設計的更易用呢?

先看下我設計的API:

import { crosData } from 'base-tools-crossDomainData';var store = new crosData({    iframeUrl:"somefile.html", //共享iframe地址,iframe有特殊要求,詳見模板文件        expire:'d,h,s' //單位天,小時,秒 默認過期時間,也可以種的時候覆蓋});store.set('key','val',{    expire:'d,h,s' //option 可帶過期時間,覆蓋expire}).then((data)=>{    //異步方法,如果種失敗,會進入catch事件    //data {val:'val',key:'key',domain:'domain'};}).catch((err)=>{    console.log(err);}); store.get('key',{    domain:'(.*).sina.cn' //可以指定域名,也可以使用(.*)來匹配正則字符串,返回的val信息會帶著domain信息,不填寫則返回本域的}).then((vals)=>{    console.log(val) //異步獲取存儲數據,可能多個,是個數組 [{},{}]}).catch((err)=>{});store.clear('key').then().catch(); //只清楚當前域下的key,不允許清除其他域下的key,只能讀

一個模塊上手快不快主要看api,所以對于一個數據共享模塊,我認為支持set,get,clear這3個方法就ok了,因為postmessage本身是個一來一回的異步的行為,包裝成promise的肯定更為合適和易用。因為localstorage不支持過期時間,所以需要一個全局的過期時間配置,當然也可以在set的時候進行單獨配置,而get的時候我們可以指定獲取某個域下的數據或者多個域下的數據,因為key名可能重復,但是域只有一個。這里就牽扯到了數據的管理,后邊單獨來說,最后clear和set的api只能種本域的數據,不可以操作其他域下的數據,get被允許。

下面我們看一下,client端的設置和API:

<!DOCTYPE html><html>    <head>        <meta charset="utf-8">        <title>crosData</title>     </head>    <body>        <script>            window.CROS = {                domain:/(.*).sina.cn/, //或者你允許的域名,支持正則和*通配符                lz:false //是否開啟lz壓縮val字符            };        </script>        <script src="http://cdn/sdk.js"></script>    </body></html>

你可以靈活在任何一個域下的一個html文檔中,引入client的js sdk,然后通過全局屬性的方式配置一個你允許被種到這個文檔所在域下的domain白名單,支持正則,然后lz是是否啟動lz-string壓縮,至于什么是lz壓縮后邊我再介紹。

到這里,一個比較通用的API設計就完成了,下面我們看一下實現原理和具體的一些問題。

三,實現原理

說起來好想蠻簡單的,但是寫起來其實并不是,我們首先需要知道postMessage怎么用,這個屬于很常見的一個API了,他有一個要點這里告訴大家,就是postMessage只能在iframe中或者使用window.open這種開啟新頁面的方式進行互相通訊,當然這里我們首先就要創建一個隱藏的iframe,進行跨域。

localstorage,跨域,h5,localstorage跨域共享

懶得拿工具畫圖了,因為流程比較清晰,這里拿文字復述一下整個通訊流程,首先父頁面創建一個隱藏的iframe,然后當執行set,get,clear等command的時候,通過postMessage來進行消息廣播,子頁面接收到消息后,解析命令,數據和回調id(postMessage無法傳遞函數和引用,兼容問題導致,最好只傳string類型,所以還需要對data做stringify)。然后當子頁面處理完localstorage的操作后,再通過postMessage把對應的cbid和data返回給父頁面,父頁面監聽message事件,處理結果。

四,編碼

嗯,所以說沒幾行,我們下面開始進行編碼了:

首先介紹一下我們用到的第三方包都有什么,為什么要用:

1,url-parse 對url進行parse解析,主要用他里面的origin屬性,因為postMessage本身對origin就有嚴格的驗證,我們要支持白名單和域名管理也需要。

2,ms 對時間簡寫做毫秒轉換的工具庫。

3, lz-string 對字符串做壓縮用的工具包,這里給大家科普一下LZ壓縮算法,首先了解LZ需要先了解RLZ,Run Length Encoding ,是一個針對無損壓縮的非常簡單的算法。它用重復字節和重復的次數來簡單描述來代替重復的字節。LZ 壓縮算法的背后是使用 RLE 算法用先前出現的相同字節序列的引用來替代。簡單的講, LZ 算法被認為是字符串匹配的算法。例如:在一段文本中某字符串經常出現,并且可以通過前面文本中出現的字符串指針來表示。

lz-string本身有優勢就是可以大大的減小你的儲存量,本身5MB的localstorage如果用來支持多域名的數據保存,很快就會被壓縮用完,但是lz-string本身比較慢,消耗比較大,大家平時在工作中如果對傳輸數據量有大小要求的話可以嘗試使用這個壓縮算法來優化字符串長度,默認是不開啟的。

4,store2 本身localstorage的api比較簡陋,為了減少代碼邏輯復雜度,這里選了一個比較流行的localstorage的實現庫來進行store的操作。

說完了第三方包我們下面看一下父頁面的js怎么來寫:

class crosData {  constructor(options) {    supportCheck();    this.options = Object.assign({      iframeUrl: '',      expire: '30d'    }, options);    this.cid = 0;    this.cbs = {};    this.iframeBeforeFuns = [];    this.parent = window;    this.origin = new url(this.options.iframeUrl).origin;    this.createIframe(this.options.iframeUrl);    addEvent(this.parent, 'message', (evt) => {      var data = JSON.parse(evt.data);      var origin = evt.origin || evt.originalEvent.origin;      //我只接收我打開的這個iframe的message,其他的都是不合法的,直接報錯      if (origin !== this.origin) {        reject('illegal origin!');        return;      }      if (data.err) {        this.cbs[data.cbid].reject(data.err);      } else {        this.cbs[data.cbid].resolve(data.ret);      }      delete this.cbs[data.cbid];    });  }  createIframe(url) {    addEvent(document, 'domready', () => {      var frame = document.createElement('iframe');      frame.style.cssText = 'width:1px;height:1px;border:0;position:absolute;left:-9999px;top:-9999px;';      frame.setAttribute('src', url);      frame.onload = () => {        this.child = frame.contentWindow;        this.iframeBeforeFuns.forEach(item => item());      }      document.body.appendChild(frame);    });  }  postHandle(type, args) {    return new Promise((resolve, reject) => {      var cbid = this.cid;      var message = {        cbid: cbid,        origin: new url(location.href).origin,        action: type,        args: args      }      this.child.postMessage(JSON.stringify(message), this.origin);      this.cbs[cbid] = {        resolve,        reject      }      this.cid++;    });  }  send(type, args) {    return new Promise(resolve => {      if (this.child) {        return this.postHandle(type, args).then(resolve);      } else {        var self = this;        this.iframeBeforeFuns.push(function() {          self.postHandle(type, args).then(resolve);        });      }    })  }  set(key, val, options) {    options = Object.assign({      expire: ms(this.options.expire)    }, options);    return this.send('set', [key, val, options]);  }  get(key, options) {    options = Object.assign({      domain: new url(location.href).origin    }, options);    return this.send('get', [key, options]);  }  clear(key) {    return this.send('clear', [key]);  }}

大概方法就這么幾個,這里有幾個關鍵點,我說一下。

1,get,set,clear方法都是統一的調用的send方法,只不過對options部分做了補齊。

2,send方法返回一個promise對象,如果iframe已經onload成功,則直接調用postHandle方法進行postMessage操作,如果iframe還在加載中,則把當前的操作推到iframeBeforeFuns數組中,用函數包裹,等待iframe onload結束后統一調用,函數包裹的也是postHandle方法。

3,postHandle方法,在發送請求前包裝data,生成cbid,origin,action和args,cbs對象保存了每個cbid下的resolve和reject,等待子頁面的postMessage返回后處理。因為postMessage不能保留引用,不能傳函數,所以這里選擇這個方法來進行關聯。

4,constructor比較好理解,當這個類被初始化的時候,我們定義了我們需要的一些options的屬性,創建iframe,然后監聽message事件,處理子頁面返回的消息。

5,在父頁面的message事件中,我們要校驗,給我發消息的必須是我打開的這個窗口iframe,否則報錯,然后根據data中的err標識來讓cbs中的resolve和reject進行執行。

6,createIframe方法中,iframe onload中的回調處理創建前 緩存的調用方法,這里注意使用了domready,因為可能body還沒解析就會進行sdk的執行。

下面是child部分的代碼:

class iframe {  set(key, val, options, origin) {    //檢查val大小,不能超過20k.    val = val.toString();    val = this.lz ? lzstring.compressToUTF16(val) : val;    var valsize = sizeof(val, 'utf16'); //localStorage 儲存使用utf16編碼計算字節    if (valsize > this.maxsize) {      return {        err: 'your store value : "' + valstr + '" size is ' + valsize + 'b, maxsize :' + this.maxsize + 'b , use utf16'      }    }    key = `${this.prefix}_${key},${new url(origin).origin}`;    var data = {      val: val,      lasttime: Date.now(),      expire: Date.now() + options.expire    };    store.set(key, data);    //大于最大儲存個數,刪除最后一次更新的    if (store.size() > this.storemax) {      var keys = store.keys();      keys = keys.sort((a, b) => {        var item1 = store.get(a),          item2 = store.get(b);        return item2.lasttime - item1.lasttime;      });      var removesize = Math.abs(this.storemax - store.size());      while (removesize) {        store.remove(keys.pop());        removesize--;      }    }    return {      ret: data    }  }  get(key, options) {    var message = {};    var keys = store.keys();    var regexp = new RegExp('^' + this.prefix + '_' + key + ',' + options.domain + '$');    message.ret = keys.filter((key) => {      return regexp.test(key);    }).map((storeKey) => {      var data = store.get(storeKey);      data.key = key;      data.domain = storeKey.split(',')[1];      if (data.expire < Date.now()) {        store.remove(storeKey);        return undefined;      } else {        //更新lasttime;        store.set(storeKey, {          val: data.val,          lasttime: Date.now(),          expire: data.expire        });      }      data.val = this.lz ? lzstring.decompressFromUTF16(data.val) : data.val;      return data;    }).filter(item => {      return !!item; //過濾undefined    });    return message;  }  clear(key, origin) {    store.remove(`${this.prefix}_${key},${origin}`);    return {};  }  clearOtherKey() {    //刪除不合法的key     var keys = store.keys();    var keyReg = new RegExp('^' + this.prefix);    keys.forEach(key => {      if (!keyReg.test(key)) {        store.remove(key);      }    });  }  constructor(safeDomain, lz) {    supportCheck();    this.safeDomain = safeDomain || /.*/;    this.prefix = '_cros';    this.clearOtherKey();    if (Object.prototype.toString.call(this.safeDomain) !== '[object RegExp]') {      throw new Error('safeDomain must be regexp');    }    this.lz = lz;    this.storemax = 100;    this.maxsize = 20 * 1024; //字節    addEvent(window, 'message', (evt) => {      var data = JSON.parse(evt.data);      var originHostName = new url(evt.origin).hostname;      var origin = evt.origin,        action = data.action,        cbid = data.cbid,        args = data.args;      //合法的廣播      if (evt.origin === data.origin && this.safeDomain.test(originHostName)) {        args.push(origin);        var whiteAction = ['set', 'get', 'clear'];        if (whiteAction.indexOf(action) > -1) {          var message = this[action].apply(this, args);          message.cbid = cbid;          window.top.postMessage(JSON.stringify(message), origin);        }      } else {        window.top.postMessage(JSON.stringify({          cbid: cbid,          err: 'Illegal domain'        }), origin);      }    });  }}

代碼也不多,這里簡單說一下各個方法的用處和組織關系:

1,constructor部分,上面的類里也進行瀏覽器特性支持檢查,然后定義了store的prefix值,最大個數和每一個key的maxsize等屬性。然后我們創建message通道,等待父頁面調用。

2,在message中,我們對發送廣播的origin進行檢查,然后對調用的方法進行檢查,調用對應的set,get,clear方法,然后把執行的結果拿到,綁定cbid,最后再postMessage發送回父頁面。

3,clearOtherKey 刪除不合法的一些store數據,只保留符合格式的數據。

4,set方法中對每一條的數據做size校驗,lz壓縮,保存的data中包含了val,key,過期時間以及更新時間(用于LRU計算)。

5,set方法中,如果儲存的ls個數超過了最大限制,這個時候需要進行刪除操作, LRU是Least Recently Used的縮寫,即最近最少使用。我們通過遍歷所有的key值,對key值做一個排序,通過lasttime,然后進行keys數組的pop操作,拿到堆棧尾部的需要被清除的key,然后逐個刪除。

6,get方法中,我們通過遍歷所有的key值,匹配到我們需要拿到的domain的域的key,然后把返回值中的key進行拆解(我們儲存時是 key,domain的格式),因為api要求返回多個符合的值,我們對過期的數據最后再做一個filter,然后使用lz解壓縮val值,保證用戶拿到的是正確結果。

以上就是我們的一個整體實現編碼過程和review,下面說一說遇到的坑。

五,一些遇到的坑

因為上面只給了主代碼,并不是完整代碼,因為本身邏輯比較清晰,花一點時間都可以寫出來的。下面說說有什么坑的地方。

1,計算localstorage的儲存值。

因為我們都知道有5MB的限制,所以每一條數據最大要求不能超過20*1024 字節,對于字節的計算,localstorage要使用utf16的編碼進行轉換,參考這篇文章: JS計算字符串所占字節數

2,兼容性

ie8下postMessage最好都傳字符串,事件需要抹平處理,JSON需要抹平處理。

3,創建iframe時的異步處理

這里之前做了個一個setTimeout的遞歸等待,后來更改成了上面的實現方法,通過onload后統一處理promise的reslove,保證promise api的統一。

4,數據保存時,空間復雜度 vs 時間復雜度。

第一個版本并不是上面的實現,我實現了3個版本:

第一個版本是保存了一個LRU的數組,為了減少時間復雜度,但是浪費了空間復雜度,而且經過測試,store的get方法耗時比較大,主要是parse的耗時。

第二個版本,為了能讓lz-string壓縮率最大化,我把所有的數據包括LRU數組保存到了一個key值上,導致數據多的時候lz-string和getItem,parse時間消耗非常大,雖然計算的時間復雜度是最低。

最后一個版本,就是上面的,我犧牲了一些時間復雜度和空間復雜度,但是因為瓶頸在于set和get的讀寫速度,單個的保存讀寫速度極快,獲取keys的方法因為底層是用的for in localstorage實現的,性能還是很不錯的,20kb存滿100條,讀寫也在1s左右,性能非常不錯。

六,總結和對比

模塊寫完了,我才知道原來還有這么一個庫: zendesk/cross-storage

但是我查看了他的api和源代碼,對比了一下實現方法,我覺得還是我這個版本考慮的比較多。

1,我的版本對域名和數據的管理有控制。

2,我的版本promise api更簡化,比它少一個onConnect,可以參考他的實現,比我寫的多多了,也沒解決這個iframe等待異步的問題。

3,不支持lz壓縮數據。

4,不支持LRU的儲存池管理,所以可能存多了造成寫不進的問題。

5,他貌似每次交互都搞一個iframe,太浪費dom操作和廣播了,我覺得一直開著并沒有什么問題,當然他可能有需求連接多個client才這么處理的。

總結

以上所述是小編給大家介紹的使用localstorage代替cookie實現跨域共享數據問題,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復大家的。在此也非常感謝大家對VeVb武林網網站的支持!


注:相關教程知識閱讀請移步到HTML教程頻道。
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
91精品国产免费久久综合| 国产精品福利视频一区二区三区| 欧美日韩国产中文| 精品中文字幕在线| 中文字幕在线官网| 国产乱国产乱老熟300部视频| 日韩免费电影网站| 国产亚洲欧美中文| 91久久精品网| 91九色在线播放| 亚洲成年人在线播放| 午夜国产在线| 久久中文免费视频| 精品欧美久久久| 日韩国产在线不卡视频| 日韩欧美一级视频| 精品人妻二区中文字幕| 在线免费播放av| 欧美一卡2卡三卡4卡5免费| 日韩欧美中文字幕在线视频| 亚洲综合在线不卡| 日韩免费看网站| 国产欧美日韩不卡| 亚洲福利精品在线| 在线视频不卡一区二区| 国产在线导航| 日韩va亚洲va欧洲va国产| 91精品国产手机| 午夜一区二区三区| 亚洲狠狠婷婷综合久久久久图片| av高清一区| 精品免费久久久| 欧美日韩中文字幕| 亚洲一区中文| 国产视频一区三区| 亚洲天堂国产视频| 日韩 欧美 综合| 一区二区日韩av| 欧美变态tickling挠脚心| av一区在线观看| 精品国产综合久久| 亚洲欧美韩国综合色| 亚洲国内精品视频| 亚洲视频日韩| 成人ww免费完整版在线观看| 91久久精品国产91久久| 国产激情久久久| 欧美日韩高清| 亚洲大胆人体av| 亚洲三级网站| 日韩在线高清视频| 一区二区中文视频| 精品视频资源站| 日韩欧美综合| 欧美日韩精品在线| 欧美在线视频第一页| 日韩在线观看a| 国产成人一二三区| 亚洲热在线观看| 精品对白一区国产伦| 日韩视频专区| www在线视频| 亚洲高清视频一区| 欧美va亚洲va日韩∨a综合色| 日韩av二区| 日韩 国产 一区| 亚洲素人一区二区| 亚洲国产欧美日韩在线| www高清在线视频日韩欧美| 97最新国自产拍视频在线完整在线看 | 欧美国产一级片| 视频一区二区不卡| 中文字幕久久av| 欧美日韩国产在线播放| 丰满少妇一区| 欧美变态tickling挠脚心| 国产在线一在线二| 日韩欧美成人一区二区三区| 日韩欧美国产综合| 精品成a人在线观看| 日韩精品视频在线看| 91欧美在线视频| 美女尤物久久精品| 日韩色在线观看| 欧美日韩视频免费播放| 91精品国产91| 国产日韩中文字幕| 久久精品久久精品亚洲人| 欧美日韩成人一区二区| 国内国产区免费视频| 精品国产网站在线观看| 久久精品国产2020观看福利| 一区二区在线高清视频| 午夜国产福利一区二区| 欧美三级网址| 午夜av一区二区| 高清日韩中文字幕| 中文字幕亚洲免费| 亚洲欧洲在线观看av| av中文在线资源| 中文字幕日韩国产| 91精品婷婷国产综合久久竹菊| 日韩三级免费观看| 日韩中文字幕视频在线观看| 视频一区二区不卡| 亚洲第一视频在线观看| 国产视频一区三区| av高清一区| 日韩激情一区| 亚洲黄色一区二区| 在线手机中文字幕| 精品国产不卡一区二区| 深夜日韩欧美| 在线日韩精品视频| 91精品国产91久久| 亚洲 国产 欧美 日韩| 91精品国产综合久久香蕉最新版 | 一级片免费在线| 精品国产99久久久久久| 中文av字幕一区| 日韩欧美中文在线视频| 中文字幕日韩高清| 日本精品在线| 在线视频你懂得一区| 日韩欧美国产成人精品免费| 国产欧美中文字幕| 国产乱一区二区| 亚洲视频日韩| 国产欧美日产一区| 国产欧美亚洲一区| 国产69精品久久久久孕妇国产69久久| 中文字幕日韩欧美在线视频| 久久久精品福利| 国产一区不卡精品| 一区三区二区视频| 国产黄色在线观看| 欧美亚洲另类制服自拍| 中文字幕一区不卡| 日韩欧美视频一区二区三区四区| 在线中文字幕网站| 91精品国产免费久久综合| 视频在线一区二区| 色猫猫国产区一区二在线视频| 久久综合九色欧美综合狠狠| 中文字幕精品在线| 国产一区精品在线| 国产乱一区二区| 欧美日韩国产专区| 日韩欧美国产成人精品免费| 亚洲成av人片一区二区密柚| 中文字幕成人乱码在线电影| 精品一区二区三区中文字幕 | 92久久精品| 亚洲一区在线观看视频| 男人的天堂网av| 欧美久久在线观看| 首页国产欧美久久| 日韩 欧美 中文| 欧美日韩国产在线观看| 中文字幕成人乱码在线电影| 首页国产欧美久久| 影音先锋中文字幕在线观看| 在线日韩中文字幕| 一级一片免费视频| 中文字幕精品一区二| 国产一区久久| 久久久91精品国产| 刘玥91精选国产在线观看| 中文字幕精品国产| 韩日中文字幕第一页| 欧美日韩综合不卡| 成人欧美亚洲| 国产午夜精品一区二区三区视频| av手机天堂| 亚洲欧美久久久| 欧美日韩综合视频网址| 欧美三级中文字幕在线观看| 红桃视频亚洲| 日韩久久在线| 久久久精品日韩| 亚洲电影中文字幕在线观看| 午夜精品一区二区三区免费视频| 亚洲 欧美综合在线网络| 一区二区91| 国产一级免费看| 欧美国产综合视频| 精品日韩一区二区三区免费视频| 欧美亚洲国产日韩2020| 成人ww免费完整版在线观看| 久久精品一二三| 欧美xxxx中国| 在线手机中文字幕| 黄色国产网站在线播放| 日韩免费视频| 91亚洲国产| 正在播放日韩精品| 欧美日韩国产中文| 精品日韩在线观看| 欧美日韩视频不卡| 日本va欧美va精品发布| va中文字幕| 国产激情久久| 中文字幕精品www乱入免费视频| 日韩精品中文字幕第1页| 国产香蕉精品视频| 久久99久久99精品中文字幕| 亚洲热线99精品视频| 日韩免费看网站| av免费观看国产| 日韩国产亚洲欧美| 亚洲一级影院| 国产一区不卡精品| 一区视频在线播放| 精品日韩在线播放| 欧美日韩综合在线观看| 一区二区91| 国产传媒久久久| 久久精品最新免费国产成人| 国产丝袜一区| 极品久久久久久| 国产福利免费观看| 亚洲欧洲在线观看av| 二区三区中文字幕| 欧美日中文字幕| 久久激情中文| 欧美啪啪一区| www.日韩免费| 视频一区二区精品的福利| 日韩视频国产视频| 国产欧美久久久久久久久| 欧美国产一级| 日本亚洲欧美天堂免费| 亚洲羞羞网站| 国产丝袜一区二区| 欧美不卡视频一区| 日本a级黄色| 中文字幕精品视频| 亚洲最新免费视频| 久久视频www| 成人一区而且| 在线免费91| 中文字幕欧美日韩精品| 国产在线视频一区二区| 日韩免费视频一区二区| 在线看av的网址| 91精品国产91久久| 中文国产字幕在线观看| 欧美成人一区二区| 亚洲综合日韩欧美| 精品久久在线| 日韩三级视频在线播放| 亚洲黄在线观看| 天堂在线中文| 欧美三级网址| 美女尤物久久精品| 日韩在线视频一区二区三区| 日本免费看黄| 国产成人精品av久久| 一区二区三区鲁丝不卡| 不卡在线一区二区| 日韩三区免费| 欧美99久久| 中文字幕精品视频| 日韩欧美专区在线| 日韩欧美国产午夜精品| 免费看日韩精品| 亚洲欧美日本另类| 中文字幕亚洲二区| 中文字幕亚洲区| 久精品在线观看| 日韩.欧美.亚洲| 天天在线视频色| 欧美日韩精品在线| 国产www在线观看| 久久精品免费在线观看| 久久久久久久久综合| 午夜一区二区视频| 快she精品国产999| 91精品久久久久久久久久| 91精品国产自产| 中文字幕日韩在线观看| 日韩欧美中文第一页| 国产不卡在线| 欧美日韩国产影片| 日韩精品一级| 久艹在线视频| 国产一级视频| 日韩一级免费视频| www日韩欧美| 日本不卡高清视频一区| 中文在线不卡视频| 国产高清视频一区二区| 亚洲九九精品| 日韩免费电影网站| 日韩国产成人| 中文字幕国产欧美| 国产在线拍揄自揄拍| 国产天堂素人系列在线视频| 日韩中文字幕视频在线| 日韩视频第二页| 中文字幕在线观看视频www| 不卡在线一区二区| 国产91欧美| 日韩精品视频在线观看视频| 欧美性生交大片免费| 日韩视频在线免费看| 亚洲丝袜一区| 欧美日韩久久不卡| 午夜少妇久久久久久久久| 国产福利在线导航| 免费视频国产一区| 精品久久久久久综合日本欧美| 深夜福利一区二区| 亚洲黄页一区| 亚洲一区在线观看免费| 日韩久久99| 亚洲社区在线| 999精品色在线播放| 亚洲高清不卡一区| 国内激情久久| 国产成人va亚洲电影| 亚洲一卡二卡在线观看| 樱花草www在线| 尤物av一区二区| 欧洲精品二区| 亚洲福利精品在线| 日韩在线观看视频一区| 国产日韩中文字幕| 中文字幕在线官网| 日韩亚洲不卡在线| 精品一二线国产| 国产三级在线| 亚洲欧洲国产视频| 中文字幕国产亚洲| 欧美国产综合在线V| 日本精品在线播放| 精品视频在线导航| 91精品国产综合久久久久久漫画 | 亚洲国产欧美91| 国产欧美日韩中文久久| 精品在线观看一区| 在线国产一级| wwwwww在线观看| 亚洲久草视频| 国产自产视频| 亚洲a一级视频| 中文字幕国产亚洲| 一区二区视频免费看| 亚洲一区中文字幕在线| а√天堂8资源中文在线| 亚洲免费播放| 日韩一级二级三级| 日韩中文字幕二区| 欧美一级欧美三级在线观看| 激情国产在线| 国产绿帽一区二区三区| 亚洲免费在线观看av| 91精品国产高清| 一区二区日韩视频| 在线视频日韩欧美| 91精品综合久久| 在线欧美日韩精品| 中文字幕2020第一页| 日韩中文在线中文网三级| 欧美另类久久久品| av不卡免费看| 日韩视频精品在线| 日韩精品视频在线播放| 日韩激情一区| 久草中文在线观看| 91精品高清| 欧美激情一区二区在线| 亚洲 欧美 精品| 日本精品免费观看高清观看| 精品久久电影| 欧美日韩综合在线观看| 国产黄在线播放| 欧美中文字幕视频在线观看| 国产丝袜欧美中文另类| 欧美日韩精品久久久| 精品国自产在线观看| 亚洲 国产 欧美 日韩| 搞黄在线观看| 一本一道综合狠狠老| 91精品视频免费在线观看| 91精品国产综合久久福利| 亚洲免费福利视频| 国产v日产∨综合v精品视频| 精品熟女一区二区三区| 国产一级久久久| 精品国产欧美成人夜夜嗨| 国产拍揄自揄精品视频麻豆| 欧美日韩中文字幕视频| 先锋男人资源站| 欧美高清视频一区二区三区| 国产福利三区| 91精品观看| 亚洲一区中文字幕在线| 国产日产一区二区三区| 日韩一级免费视频|