整合營銷服務(wù)商

          電腦端+手機端+微信端=數(shù)據(jù)同步管理

          免費咨詢熱線:

          在JavaScript中使用Cookie

          avaScript Cookie

          Cookie用于存儲web頁面的用戶信息

          什么是cookie?

          Cookie是一些數(shù)據(jù),存儲于電腦上的文本中。

          當(dāng)web服務(wù)器向瀏覽器發(fā)送web頁面時,鏈接關(guān)閉后,服務(wù)端不會記錄用戶的信息

          Cookie的作用是"如何記錄客服端的用戶信息"

          1. 當(dāng)用戶訪問web頁面時,他的名字可以記錄在cookie中。

          2. 用戶下一次訪問該頁面時,可以在cookie中讀取用戶記錄。

          Cookie以名/值得形式存儲,如下所示

          Username=John Doe

          當(dāng)瀏覽器從服務(wù)器上請求web頁面時,屬于該頁面的cookie會被添加在該請求中。服務(wù)端通過這種方式來獲取用戶信息

          使用JavaScript創(chuàng)建Cookie

          JavaScript可以使用document.cookie屬性來創(chuàng)建、讀取、刪除cookie!

          JavaScript中,創(chuàng)建cookie如下所示:

          document.cookie="username=John Doe";

          你開可以為cookie添加一個過期時間(以UTC或GMT時間)。默認(rèn)情況下,cookie在瀏覽器關(guān)閉時刪除:

          document.cookie="username=John doe;expires=Thu,18 Dec 2043 12:00:00 GMT";

          Path參數(shù)告訴瀏覽器cookie的路徑。默認(rèn)情況下,cookie屬于當(dāng)前頁面

          document.cookie="username=John Doe;expires=Thu,18 Dec 2043 12:00:00 GMT;path=/";

          使用JavaScript讀取cookie

          在JavaScript中,可以使用以下代碼來讀取cookie

          var x=document.cookie;

          document.cookie將以字符串的方式返回所有cookie,類型格式:

          cookie1=value;cookie2=value;cookie3=value;

          使用JavaScript修改cookie

          在使用JavaScript中,修改cookie類似于創(chuàng)建cookie,如下所示:

          document.cookie="username=John Smith;expires=Thu, 18 Dec 2043 12:00:00 GMT;path=/";

          舊的cookie將被覆蓋

          使用JavaScript刪除Cookie

          刪除cookie非常簡單。只需要設(shè)置expires參數(shù)為以前的時間即可,如下所示,設(shè)置為Thu,01 Jan 1970 00:00:00 GMT:

          document.cookie="username=;expiress=Thu. 01 Jan 1970 00:00: GMT";

          *當(dāng)你刪除時不必指定cookie值

          Cookie 字符串

          document.cookie 屬性看起來像一個普通的文本字符串,其實它不是。

          即使您在 document.cookie 中寫入一個完整的 cookie 字符串, 當(dāng)您重新讀取該 cookie 信息時,cookie 信息是以名/值對的形式展示的。

          如果您設(shè)置了新的 cookie,舊的 cookie 不會被覆蓋。 新 cookie 將添加到 document.cookie 中,所以如果您重新讀取document.cookie,您將獲得如下所示的數(shù)據(jù):

          cookie1=value; cookie2=value;

          *如果您需要查找一個指定 cookie 值,您必須創(chuàng)建一個JavaScript 函數(shù)在 cookie 字符串中查找 cookie 值。

          JavaScript Cookie實例

          在以下實例中,我們將創(chuàng)建cookie來存儲訪問者的名稱

          首先,訪問者訪問web頁面,他將被要求填寫自己的名字。改名字會存儲在cookie中。

          訪問者下一次訪問頁面時,他會看到一個歡迎的消息。

          在這個實例中我們會創(chuàng)建3個JavaScript函數(shù)

          1、 設(shè)置cookie值得函數(shù)

          2、 獲取cookie值得函數(shù)

          3、 檢測cookie值得函數(shù)

          設(shè)置cookie值得函數(shù)

          首先,創(chuàng)建一個函數(shù)用于存儲訪問者的名字:

          函數(shù)解析:

          以上的函數(shù)參數(shù)中,cookie的名稱為出那么,cookie的值為cvalue,并設(shè)置了cookie的過期時間expires。

          該函數(shù)設(shè)置了cookie名,cookie值、cookie過期時間。

          獲取cookie值得函數(shù)

          然后,創(chuàng)建一個函數(shù)用戶返回指定的cookie值:

          函數(shù)解析:

          cookie名的參數(shù)為cname。

          創(chuàng)建一個文本變量用于檢索指定的cookie:cname+"="。

          使用分號來分割document.cookie字符串,并將分割后的字符串?dāng)?shù)組賦給ca(ca=document.cookie.split(";"))。

          循環(huán)ca數(shù)組(i=0;i<ca.length;i++),然后讀取數(shù)組中的每個值,并除去前后空格(c=ca[i].trim())。

          如果找到cookie(c.indexOf(name)==0),返回cookie的值(c.substring(name.length;c.length)。

          如果沒有找到cookie,返回""。

          檢測cookie值的函數(shù)

          創(chuàng)建一個檢測cookie的函數(shù)。

          如果設(shè)置了cookie,將顯示一個問候信息。

          如果沒有設(shè)置cookie,將會顯示一個彈窗用于詢問防問者的名字,并調(diào)用setCookie函數(shù)將訪問者的名字存儲365天;

          實例:

          eb瀏覽器和服務(wù)器使用HTTP協(xié)議進行通信,并且HTTP是一種無狀態(tài)協(xié)議。但對于一個商業(yè)網(wǎng)站它需要保持不同的頁面間的會話信息。例如在完成多頁后,一個用戶注冊結(jié)束。但是,如何保持所有網(wǎng)頁用戶的會話信息。

          在許多情況下,使用Cookie來記憶和跟蹤的喜好,采購,傭金,并要求更好的訪問體驗或網(wǎng)站的統(tǒng)計數(shù)據(jù)等信息的最有效的方法。

          它是如何工作的 ?

          服務(wù)器發(fā)送一些數(shù)據(jù)到訪問者的瀏覽器以cookie的形式。該瀏覽器可以接受cookie。如果是這樣,它是為訪問者存儲在硬盤驅(qū)動器上的一個純文本的記錄。現(xiàn)在,當(dāng)訪問者到達您的網(wǎng)站其他頁面,瀏覽器發(fā)送相同cookie到服務(wù)器進行檢索。一旦檢索到,服務(wù)器知道/記得剛才發(fā)生了什么存儲。

          Cookies有5個可變長度字段的純文本數(shù)據(jù)記錄:

          Expires : cookie將過期的日期。如果這是空白的,那么就是當(dāng)訪問者退出瀏覽器cookie將到期。

          Domain : 網(wǎng)站的域名。

          Path : 路徑設(shè)置 cookie 目錄或網(wǎng)頁。如果想要從任何目錄或頁面,那么cookie 是空的。

          Secure : 如果該字段包含“安全”二字,那么cookie僅可檢索到一個安全的服務(wù)器。如果該字段為空,沒有限制存在。

          Name=Value : Cookie設(shè)置在鍵和值對的形式來獲取。

          Cookie最初設(shè)計用于CGI編程和cookies的數(shù)據(jù)是在Web瀏覽器和Web服務(wù)器之間自動傳輸?shù)模栽诜?wù)器上的CGI腳本可以讀取和寫入存儲在客戶端上的cookie的值。

          JavaScript的也可以操作使用文檔對象的cookie屬性。 JavaScript可以讀取,創(chuàng)建,修改和刪除適用于當(dāng)前網(wǎng)頁的cookie或Cookies。

          儲存Cookies:

          創(chuàng)建一個cookie的最簡單的方法是一個字符串值分配到document.cookie對象,它是這樣的:

          語法

          document.cookie = "key1=value1;key2=value2;expires=date";1復(fù)制代碼類型:[javascript]

          在這里,expires屬性選項。如果提供這個屬性有一個有效的日期或時間,那么cookie將在給定的日期或時間滿期,而后cookies的值將無法訪問到。

          注意:Cookie的值可能不包括分號,逗號或空白。出于這個原因,可能需要使用JavaScript 的 escape()函數(shù)將其存儲的值在cookie之前進行編碼。如果這樣做,當(dāng)讀取cookie的值時也必須使用相應(yīng)的unescape()函數(shù)。

          例子:

          下面是示例,設(shè)置在輸入客戶名稱在cookie。

          <html>
          <head>
          <script type="text/javascript">
          <!--
          function WriteCookie()
          {
             if( document.myform.customer.value == "" ){
                alert("Enter some value!");
                return;
             }
          
             cookievalue= escape(document.myform.customer.value) + ";";
             document.cookie="name=" + cookievalue;
             alert("Setting Cookies : " + "name=" + cookievalue );
          }
          //-->
          </script>
          </head>
          <body>
          <form name="myform" action="">
          Enter name: <input type="text" name="customer"/>
          <input type="button" value="Set Cookie" onclick="WriteCookie();"/>
          </form>
          </body>
          </html>
          
          123456789101112131415161718192021222324252627復(fù)制代碼類型:[javascript]

          這將產(chǎn)生以下結(jié)果。現(xiàn)在進入一些在文本框中,然后按下按鈕“Set Cookie”設(shè)置cookie。

          現(xiàn)在,有一個cookie叫name。可以設(shè)置使用由逗號分隔multiplekey= value對應(yīng)多個cookie。

          你將在下一節(jié)學(xué)習(xí)如何讀取該cookie。

          讀取Cookies:

          讀取cookie和寫入一樣簡單,因為 document.cookieobject 的值是cookie。所以每當(dāng)要訪問cookie,可以使用這個字符串。

          該字符串 document.cookie將繼續(xù)由分號,其中name是一個cookie的名稱,值是它的字符串值分隔的名稱=值對的列表。

          您可以使用字符串split()函數(shù)來打破串入鍵和值如下:

          例子:

          下面是一個例子,以獲得上一節(jié)設(shè)置的cookie。

          <html>
          <head>
          <script type="text/javascript">
          <!--
          function ReadCookie()
          {
             var allcookies = document.cookie;
             alert("All Cookies : " + allcookies );
          
             // Get all the cookies pairs in an array
             cookiearray  = allcookies.split(';');
          
             // Now take key value pair out of this array
             for(var i=0; i<cookiearray.length; i++){
                name = cookiearray[i].split('=')[0];
                value = cookiearray[i].split('=')[1];
                alert("Key is : " + name + " and Value is : " + value);
             }
          }
          //-->
          </script>
          </head>
          <body>
          <form name="myform" action="">
          <input type="button" value="Get Cookie" onclick="ReadCookie()"/>
          </form>
          </body>
          </html>
          
          123456789101112131415161718192021222324252627282930復(fù)制代碼類型:[javascript]

          注:這里的length 是Array類的方法,該方法返回一個數(shù)組的長度。我們將在一個單獨的章節(jié)討論數(shù)組。到那個時候,請盡量消化它。

          這將產(chǎn)生以下結(jié)果。現(xiàn)在按按鈕“Get Cookie”,看看在上一節(jié)中如何設(shè)置cookie。


          設(shè)置Cookies的過期日期:

          可以通過設(shè)置的到期日期和保存cookie中的失效日期延長超出當(dāng)前瀏覽器會話cookie中的壽命。這可以通過設(shè)置expires屬性的日期和時間來完成。

          例子:

          下面的例子演示了如何設(shè)置cookie1個月后過期:

          <html>
          <head>
          <script type="text/javascript">
          <!--
          function WriteCookie()
          {
             var now = new Date();
             now.setMonth( now.getMonth() + 1 ); 
             cookievalue = escape(document.myform.customer.value) + ";"
             document.cookie="name=" + cookievalue;
             document.cookie = "expires=" + now.toUTCString() + ";"
             alert("Setting Cookies : " + "name=" + cookievalue );
          }
          //-->
          </script>
          </head>
          <body>
          <form name="formname" action="">
          Enter name: <input type="text" name="customer"/>
          <input type="button" value="Set Cookie" onclick="WriteCookie()"/>
          </form>
          </body>
          </html>
          
          12345678910111213141516171819202122232425復(fù)制代碼類型:[javascript]

          刪除Cookie:

          有時如果想刪除一個cookie,以便后續(xù)嘗試讀取cookie返回什么。要做到這一點,你只需要在到期日設(shè)置在過去的某個時間。

          例子:

          下面的例子演示了如何通過設(shè)置有效期限一個月以前刪除cookie:

          <html>
          <head>
          <script type="text/javascript">
          <!--
          function WriteCookie()
          {
             var now = new Date();
             now.setMonth( now.getMonth() - 1 ); 
             cookievalue = escape(document.myform.customer.value) + ";"
             document.cookie="name=" + cookievalue;
             document.cookie = "expires=" + now.toUTCString() + ";"
             alert("Setting Cookies : " + "name=" + cookievalue );
          }
          //-->
          </script>
          </head>
          <body>
          <form name="formname" action="">
          Enter name: <input type="text" name="customer"/>
          <input type="button" value="Set Cookie" onclick="WriteCookie()"/>
          </form>
          </body>
          </html>
          
          12345678910111213141516171819202122232425復(fù)制代碼類型:[javascript]

          注:不設(shè)置日期,可以使用setTime()函數(shù)看到新值。


          型:[javascript]

          注:不設(shè)置日期,可以使用setTime()函數(shù)看到新值。

          開課吧廣場-人才學(xué)習(xí)交流平臺

          eb 開發(fā)中的 cookie 是什么?

          cookie 是后端可以存儲在用戶瀏覽器中的小塊數(shù)據(jù)。 Cookie 最常見用例包括用戶跟蹤,個性化以及身份驗證。

          Cookies 具有很多隱私問題,多年來一直受到嚴(yán)格的監(jiān)管。

          在本文中,主要側(cè)重于技術(shù)方面:學(xué)習(xí)如何在前端和后端創(chuàng)建,使用 HTTP cookie。

          后端配置

          后端示例是Flask編寫的。如果你想跟著學(xué)習(xí),可以創(chuàng)建一個新的Python虛擬環(huán)境,移動到其中并安裝Flask

          mkdir cookies && cd $_
          
          python3 -m venv venv
          source venv/bin/activate
          
          pip install Flask
          

          在項目文件夾中創(chuàng)建一個名為flask app.py的新文件,并使用本文的示例在本地進行實驗。

          誰創(chuàng)建 cookies ?

          首先,cookies 從何而來?誰創(chuàng)建 cookies ?

          雖然可以使用document.cookie在瀏覽器中創(chuàng)建 cookie,但大多數(shù)情況下,后端的責(zé)任是在將響應(yīng)客戶端請求之前在請求中設(shè)置 cookie。

          后端是指可以通過以下方式創(chuàng)建 Cookie:

          • 后端實際應(yīng)用程序的代碼(Python、JavaScript、PHP、Java)
          • 響應(yīng)請求的Web服務(wù)器(Nginx,Apache)

          后端可以在 HTTP 請求求中 Set-Cookie 屬性來設(shè)置 cookie,它是由鍵/值對以及可選屬性組成的相應(yīng)字符串:

          Set-Cookie: myfirstcookie=somecookievalue
          

          什么時候需要創(chuàng)建 cookie?這取決于需求。

          cookie 是簡單的字符串。在項目文件夾中創(chuàng)建一個名為flask_app.py的Python文件,并輸入以下內(nèi)容:

          from flask import Flask, make_response
          
          app = Flask(__name__)
          
          
          @app.route("/index/", methods=["GET"])
          def index():
              response = make_response("Here, take some cookie!")
              response.headers["Set-Cookie"] = "myfirstcookie=somecookievalue"
              return response
          

          然后運行應(yīng)用程序:

          FLASK_ENV=development FLASK_APP=flask_app.py flask run
          

          當(dāng)該應(yīng)用程序運行時,用戶訪問http://127.0.0.1:5000/index/,后端將設(shè)置一個具有鍵/值對的名為Set-Cookie的響應(yīng)標(biāo)頭。

          (127.0.0.1:5000是開發(fā)中的 Flask 應(yīng)用程序的默認(rèn)偵聽地址/端口)。

          Set-Cookie標(biāo)頭是了解如何創(chuàng)建cookie的關(guān)鍵:

          response.headers["Set-Cookie"] = "myfirstcookie=somecookievalue"
          

          大多數(shù)框架都有自己設(shè)置 cookie 的方法,比如Flask的set_cookie()。

          如何查看 cookies ?

          訪問http://127.0.0.1:5000/index/后,后端將在瀏覽器中設(shè)置cookie。要查看此cookie,可以從瀏覽器的控制臺調(diào)用document.cookie:

          或者可以在開發(fā)人員工具中選中Storage選項卡。單擊cookie,會看到 cookie 具體的內(nèi)容:

          在命令行上,還可以使用curl查看后端設(shè)置了哪些 cookie

          curl -I http://127.0.0.1:5000/index/
          

          可以將 Cookie 保存到文件中以供以后使用:

          curl -I http://127.0.0.1:5000/index/ --cookie-jar mycookies
          

          在 stdout 上顯示 cookie:

          curl -I http://127.0.0.1:5000/index/ --cookie-jar -
          

          請注意,沒有HttpOnly屬性的cookie,在瀏覽器中可以使用document.cookie上訪問,如果設(shè)置了 HttpOnly 屬性,document.cookie就讀取不到。

          Set-Cookie: myfirstcookie=somecookievalue; HttpOnly
          

          現(xiàn)在,該cookie 仍將出現(xiàn)在 Storage 選項卡中,但是 document.cookie返回的是一個空字符串。

          從現(xiàn)在開始,為方便起見,使用Flask的 response.set_cookie() 在后端上創(chuàng)建 cookie。

          我有一個 cookie,現(xiàn)在怎么辦?

          你的瀏覽器得到一個 cookie。現(xiàn)在怎么辦呢?一旦有了 cookie,瀏覽器就可以將cookie發(fā)送回后端。

          這有許多用途發(fā)如:用戶跟蹤、個性化,以及最重要的身份驗證。

          例如,一旦你登錄網(wǎng)站,后端就會給你一個cookie:

          Set-Cookie: userid=sup3r4n0m-us3r-1d3nt1f13r
          

          為了在每個后續(xù)請求中正確識別 我們的身份,后端會檢查來自請求中瀏覽器的 cookie

          要發(fā)送Cookie,瀏覽器會在請求中附加一個Cookie標(biāo)頭:

          Cookie: userid=sup3r4n0m-us3r-1d3nt1f13r
          

          cookie 可以設(shè)置過期時間: Max-Age 和 expires

          默認(rèn)情況下,cookie 在用戶關(guān)閉會話時即關(guān)閉瀏覽器時過期。要持久化cookie,我們可以通過expires或Max-Age屬性

          Set-Cookie: myfirstcookie=somecookievalue; expires=Tue, 09 Jun 2020 15:46:52 GMT; Max-Age=1209600
          

          注意:Max-Age優(yōu)先于expires

          cookie的作用域是網(wǎng)站路徑: path 屬性

          考慮該后端,該后端在訪問http://127.0.0.1:5000/時為其前端設(shè)置了一個新的 cookie。相反,在其他兩條路徑上,我們打印請求的cookie:

          from flask import Flask, make_response, request
          
          app = Flask(__name__)
          
          
          @app.route("/", methods=["GET"])
          def index():
              response = make_response("Here, take some cookie!")
              response.set_cookie(key="id", value="3db4adj3d", path="/about/")
              return response
          
          
          @app.route("/about/", methods=["GET"])
          def about():
              print(request.cookies)
              return "Hello world!"
          
          
          @app.route("/contact/", methods=["GET"])
          def contact():
              print(request.cookies)
              return "Hello world!"
          

          運行該應(yīng)用程序:

          FLASK_ENV=development FLASK_APP=flask_app.py flask run
          

          在另一個終端中,如果我們與根路由建立連接,則可以在Set-Cookie中看到cookie:

          curl -I http://127.0.0.1:5000/ --cookie-jar cookies
          
          HTTP/1.0 200 OK
          Content-Type: text/html; charset=utf-8
          Content-Length: 23
          Set-Cookie: id=3db4adj3d; Path=/about/
          Server: Werkzeug/1.0.1 Python/3.8.3
          Date: Wed, 27 May 2020 09:21:37 GMT
          

          請注意,此時 cookie 具有Path屬性:

          Set-Cookie: id=3db4adj3d; Path=/about/
          

          /about/ 路由并保存 cookit

          curl -I http://127.0.0.1:5000/about/ --cookie cookies
          

          在 Flask 應(yīng)用程序的終端中運行如下命令,可以看到:

          ImmutableMultiDict([('id', '3db4adj3d')])
          127.0.0.1 - - [27/May/2020 11:27:55] "HEAD /about/ HTTP/1.1" 200 -
          

          正如預(yù)期的那樣,cookie 返回到后端。現(xiàn)在嘗試訪問 /contact/ 路由:

          url -I http://127.0.0.1:5000/contact/ --cookie cookies
          

          在 Flask 應(yīng)用程序的終端中運行如下命令,可以看到:

          ImmutableMultiDict([])
          127.0.0.1 - - [27/May/2020 11:29:00] "HEAD /contact/ HTTP/1.1" 200 -
          

          這說明啥?cookie 的作用域是Path 。具有給定路徑屬性的cookie不能被發(fā)送到另一個不相關(guān)的路徑,即使這兩個路徑位于同一域中。

          這是cookie權(quán)限的第一層。

          在cookie創(chuàng)建過程中省略Path時,瀏覽器默認(rèn)為/。

          cookie 的作用域是域名: domain 屬性

          cookie 的 Domain 屬性的值控制瀏覽器是否應(yīng)該接受cookie以及cookie返回的位置。

          讓我們看一些例子。

          主機不匹配(錯誤的主機)

          查看 https://serene-bastion-01422.herokuapp.com/get-wrong-domain-cookie/設(shè)置的cookie:

          Set-Cookie: coookiename=wr0ng-d0m41n-c00k13; Domain=api.valentinog.com
          

          這里的 cookie 來自serene-bastion-01422.herokuapp.com,但是Domain屬性具有api.valentinog.com

          瀏覽器沒有其他選擇來拒絕這個 cookie。比如 Chrome 會給出一個警告(Firefox沒有)

          主機不匹配(子域名)

          查看 https://serene-bastion-01422.herokuapp.com/get-wrong-subdomain-cookie/設(shè)置的cookie:

          Set-Cookie: coookiename=wr0ng-subd0m41n-c00k13; Domain=secure-brushlands-44802.herokuapp.com
          

          這里的 Cookie 來自serene-bastion-01422.herokuapp.com,但**“Domain”**屬性是secure-brushlands-44802.herokuapp.com。

          它們在相同的域上,但是子域名不同。同樣,瀏覽器也拒絕此cookie:

          主機匹配(整個域)

          查看 https://www.valentinog.com/get-domain-cookie.html設(shè)置的cookie:

          set-cookie: cookiename=d0m41n-c00k13; Domain=valentinog.com
          

          此cookie是使用 Nginx add_header在Web服務(wù)器上設(shè)置的:

          add_header Set-Cookie "cookiename=d0m41n-c00k13; Domain=valentinog.com";
          

          這里使用 Nginx 中設(shè)置cookie的多種方法。Cookie 是由 Web 服務(wù)器或應(yīng)用程序的代碼設(shè)置的,對于瀏覽器來說無關(guān)緊要。

          重要的是 cookie 來自哪個域。

          在此瀏覽器將愉快地接受cookie,因為Domain中的主機包括cookie所來自的主機。

          換句話說,valentinog.com包括子域名www.valentinog.com。

          同時,對valentinog.com的新請求,cookie 都會攜帶著,以及任何對valentinog.com子域名的請求。

          這是一個附加了Cookie的 www 子域請求:

          下面是對另一個自動附加cookie的子域的請求

          Cookies 和公共后綴列表

          查看 https://serene-bastion-01422.herokuapp.com/get-domain-cookie/:設(shè)置的 cookie:

          Set-Cookie: coookiename=d0m41n-c00k13; Domain=herokuapp.com
          

          這里的 cookie 來自serene-bas-01422.herokuapp.com,Domain 屬性是herokuapp.com。瀏覽器在這里應(yīng)該做什么

          你可能認(rèn)為serene-base-01422.herokuapp.com包含在herokuapp.com域中,因此瀏覽器應(yīng)該接受cookie。

          相反,它拒絕 cookie,因為它來自公共后綴列表中包含的域。

          Public Suffix List(公共后綴列表)。此列表列舉了頂級域名和開放注冊的域名。瀏覽器禁止此列表上的域名被子域名寫入Cookie。

          主機匹配(子域)

          查看 https://serene-bastion-01422.herokuapp.com/get-subdomain-cookie/:設(shè)置的 cookie:

          Set-Cookie: coookiename=subd0m41n-c00k13
          

          當(dāng)域在cookie創(chuàng)建期間被省略時,瀏覽器會默認(rèn)在地址欄中顯示原始主機,在這種情況下,我的代碼會這樣做:

          response.set_cookie(key="coookiename", value="subd0m41n-c00k13")
          

          當(dāng) Cookie 進入瀏覽器的 Cookie 存儲區(qū)時,我們看到已應(yīng)用Domain :

          現(xiàn)在,我們有來自serene-bastion-01422.herokuapp.com 的 cookie, 那 cookie 現(xiàn)在應(yīng)該送到哪里?

          如果你訪問https://serene-bastion-01422.herokuapp.com/,則 cookie 隨請求一起出現(xiàn):

          但是,如果訪問herokuapp.com,則 cookie 不會隨請求一起出現(xiàn):

          概括地說,瀏覽器使用以下啟發(fā)式規(guī)則來決定如何處理cookies(這里的發(fā)送者主機指的是你訪問的實際網(wǎng)址):

          • 如果“Domain”中的域或子域與訪問的主機不匹配,則完全拒絕 Cookie
          • 如果 Domain 的值包含在公共后綴列表中,則拒絕 cookie
          • 如果Domain 中的域或子域與訪問在主機匹配,則接受 Cookie

          一旦瀏覽器接受了cookie,并且即將發(fā)出請求,它就會說:

          • 如果請求主機與我在Domain中看到的值完全匹配,則會回傳 cookie
          • 如果請求主機是與我在“Domain”中看到的值完全匹配的子域,則將回傳 cookie
          • 如果請求主機是sub.example.dev之類的子域,包含在example.dev之類的 Domain 中,則將回傳 cookie
          • 如果請求主機是例如example.dev之類的主域,而 Domain 是sub.example.dev之類,則不會回傳cookie。

          Domain 和 Path 屬性一直是 cookie 權(quán)限的第二層。

          Cookies可以通過AJAX請求傳遞

          Cookies 可以通過AJAX請求傳播。AJAX 請求是使用 JS (XMLHttpRequest或Fetch)進行的異步HTTP請求,用于獲取數(shù)據(jù)并將其發(fā)送回后端。

          考慮 Flask的另一個示例,其中有一個模板,該模板又會加載 JS 文件:

          from flask import Flask, make_response, render_template
          
          app = Flask(__name__)
          
          
          @app.route("/", methods=["GET"])
          def index():
              return render_template("index.html")
          
          
          @app.route("/get-cookie/", methods=["GET"])
          def get_cookie():
              response = make_response("Here, take some cookie!")
              response.set_cookie(key="id", value="3db4adj3d")
              return response
          

          以下是 templates/index.html 模板:

          <!DOCTYPE html>
          <html lang="en">
          <head>
              <meta charset="UTF-8">
              <title>Title</title>
          </head>
          <body>
          <button>FETCH</button>
          </body>
          <script src="{{ url_for('static', filename='index.js') }}"></script>
          </html>
          

          下面是 static/index.js 的內(nèi)容:

          const button = document.getElementsByTagName("button")[0];
          
          button.addEventListener("click", function() {
            getACookie();
          });
          
          function getACookie() {
            fetch("/get-cookie/")
              .then(response => {
                // make sure to check response.ok in the real world!
                return response.text();
              })
              .then(text => console.log(text));
          }
          

          當(dāng)訪問http://127.0.0.1:5000/時,我們會看到一個按鈕。通過單擊按鈕,我們向/get-cookie/發(fā)出獲取請求并獲取Cookie。正如預(yù)期的那樣,cookie 落在瀏覽器的 Cookie storage中。

          對 Flask 應(yīng)用程序進行一些更改,多加一個路由:

          from flask import Flask, make_response, request, render_template, jsonify
          
          app = Flask(__name__)
          
          
          @app.route("/", methods=["GET"])
          def index():
              return render_template("index.html")
          
          
          @app.route("/get-cookie/", methods=["GET"])
          def get_cookie():
              response = make_response("Here, take some cookie!")
              response.set_cookie(key="id", value="3db4adj3d")
              return response
          
          
          @app.route("/api/cities/", methods=["GET"])
          def cities():
              if request.cookies["id"] == "3db4adj3d":
                  cities = [{"name": "Rome", "id": 1}, {"name": "Siena", "id": 2}]
                  return jsonify(cities)
              return jsonify(msg="Ops!")
          

          另外,調(diào)整一下 JS 代碼,用于下請求剛新增的路由:

          const button = document.getElementsByTagName("button")[0];
          
          button.addEventListener("click", function() {
            getACookie().then(() => getData());
          });
          
          function getACookie() {
            return fetch("/get-cookie/").then(response => {
              // make sure to check response.ok in the real world!
              return Promise.resolve("All good, fetch the data");
            });
          }
          
          function getData() {
            fetch("/api/cities/")
              .then(response => {
                // make sure to check response.ok in the real world!
                return response.json();
              })
              .then(json => console.log(json));
          

          當(dāng)訪問http://127.0.0.1:5000/時,我們會看到一個按鈕。通過單擊按鈕,我們向/get-cookie/發(fā)出獲取請求以獲取Cookie。Cookie出現(xiàn)后,我們就會對/api/cities/再次發(fā)出Fetch請求。

          在瀏覽器的控制臺中,可以看到請求回來 的數(shù)據(jù)。另外,在開發(fā)者工具的Network選項卡中,可以看到一個名為Cookie的頭,這是通過AJAX請求傳給后端。

          只要前端與后端在同一上下文中,在前端和后端之間來回交換cookie就可以正常工作:我們說它們來自同源。

          這是因為默認(rèn)情況下,F(xiàn)etch 僅在請求到達觸發(fā)請求的來源時才發(fā)送憑據(jù),即 Cookie。

          cookie 不能總是通過AJAX請求傳遞

          考慮另一種情況,在后端獨立運行,可以這樣啟動應(yīng)用程序:

          FLASK_ENV=development FLASK_APP=flask_app.py flask run
          

          現(xiàn)在,在 Flask 應(yīng)用程序之外的其他文件夾中,創(chuàng)建index.html:

          <!DOCTYPE html>
          <html lang="en">
          <head>
              <meta charset="UTF-8">
              <title>Title</title>
          </head>
          <body>
          <button>FETCH</button>
          </body>
          <script src="index.js"></script>
          </html>
          

          使用以下代碼在同一文件夾中創(chuàng)建一個名為index.js的 JS 文件:

          
          button.addEventListener("click", function() {
            getACookie().then(() => getData());
          });
          
          function getACookie() {
            return fetch("http://localhost:5000/get-cookie/").then(response => {
              // make sure to check response.ok in the real world!
              return Promise.resolve("All good, fetch the data");
            });
          }
          
          function getData() {
            fetch("http://localhost:5000/api/cities/")
              .then(response => {
                // make sure to check response.ok in the real world!
                return response.json();
              })
              .then(json => console.log(json));
          }
          

          在同一文件夾中,從終端運行:

          npx serve
          

          此命令為您提供了要連接的本地地址/端口,例如http://localhost:42091/。訪問頁面并嘗試在瀏覽器控制臺打開的情況下單擊按鈕。在控制臺中,可以看到:

          Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at http://localhost:5000/get-cookie/. (Reason: CORS header ‘Access-Control-Allow-Origin’ missing)
          

          因為 http://localhost:5000/ 與http://localhost:42091/.不同。它們是不同的域,因此會 CORS 的限制。

          處理 CORS

          CORS 是一個 W3C 標(biāo)準(zhǔn),全稱是“跨域資源共享”(Cross-origin resource sharing)。它允許瀏覽器向跨域的服務(wù)器,發(fā)出XMLHttpRequest請求,從而克服了 AJAX 只能同源使用的限制。

          整個 CORS 通信過程,都是瀏覽器自動完成,不需要用戶參與。對于開發(fā)者來說,CORS 通信與普通的 AJAX 通信沒有差別,代碼完全一樣。瀏覽器一旦發(fā)現(xiàn) AJAX 請求跨域,就會自動添加一些附加的頭信息,有時還會多出一次附加的請求,但用戶不會有感知。因此,實現(xiàn) CORS 通信的關(guān)鍵是服務(wù)器。只要服務(wù)器實現(xiàn)了 CORS 接口,就可以跨域通信。

          默認(rèn)情況下,除非服務(wù)器設(shè)置了Access-Control-Allow-Origin的特定HTTP標(biāo)頭,否則瀏覽器將阻止AJAX對非相同來源的遠程資源的請求。

          要解決此第一個錯誤,我們需要為Flask配置CORS:

          pip install flask-cors
          

          然后將 CORS 應(yīng)用于 Flask:

          from flask import Flask, make_response, request, render_template, jsonify
          from flask_cors import CORS
          
          app = Flask(__name__)
          CORS(app=app)
          
          
          @app.route("/", methods=["GET"])
          def index():
              return render_template("index.html")
          
          
          @app.route("/get-cookie/", methods=["GET"])
          def get_cookie():
              response = make_response("Here, take some cookie!")
              response.set_cookie(key="id", value="3db4adj3d")
              return response
          
          
          @app.route("/api/cities/", methods=["GET"])
          def cities():
              if request.cookies["id"] == "3db4adj3d":
                  cities = [{"name": "Rome", "id": 1}, {"name": "Siena", "id": 2}]
                  return jsonify(cities)
              return jsonify(msg="Ops!")
          

          現(xiàn)在嘗試在瀏覽器控制臺打開的情況下再次單擊按鈕。在控制臺中你應(yīng)該看到

          Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at http://localhost:5000/api/cities/. (Reason: CORS header ‘Access-Control-Allow-Origin’ missing)
          

          盡管我們犯了同樣的錯誤,但這次的罪魁禍?zhǔn)资堑诙€路由。

          你可以通過查看 “Network” 標(biāo)簽中的請求來確認(rèn),沒有發(fā)送此類Cookie:

          為了在不同來源的Fetch請求中包含cookie,我們必須提credentials 標(biāo)志(默認(rèn)情況下,它是相同來源)。

          如果沒有這個標(biāo)志,F(xiàn)etch 就會忽略 cookie,可以這樣修復(fù):

          const button = document.getElementsByTagName("button")[0];
          
          button.addEventListener("click", function() {
            getACookie().then(() => getData());
          });
          
          function getACookie() {
            return fetch("http://localhost:5000/get-cookie/", {
              credentials: "include"
            }).then(response => {
              // make sure to check response.ok in the real world!
              return Promise.resolve("All good, fetch the data");
            });
          }
          
          function getData() {
            fetch("http://localhost:5000/api/cities/", {
              credentials: "include"
            })
              .then(response => {
                // make sure to check response.ok in the real world!
                return response.json();
              })
              .then(json => console.log(json));
          }
          

          credentials: "include" 必須在第一個 Fetch 請求中出現(xiàn),才能將Cookie保存在瀏覽器的Cookie storage 中:

          fetch("http://localhost:5000/get-cookie/", {
              credentials: "include"
            })
          

          它還必須在第二個請求時出現(xiàn),以允許將cookie傳輸回后端

            fetch("http://localhost:5000/api/cities/", {
              credentials: "include"
            })
          

          再試一次,我們還需要在后端修復(fù)另一個錯誤:

          Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at http://localhost:5000/get-cookie/. (Reason: expected ‘true’ in CORS header ‘Access-Control-Allow-Credentials’).
          

          為了允許在CORS請求中傳輸cookie,后端還需要設(shè)置 Access-Control-Allow-Credentials標(biāo)頭。

          CORS(app=app, supports_credentials=True)
          

          要點:為了使Cookie在不同來源之間通過AJAX請求傳遞,可以這樣做:

          • credentials: "include" 用于前端的 fetch 請求中
          • Access-Control-Allow-Credentials 和 Access-Control-Allow-Origin 用于后端

          cookie可以通過AJAX請求傳遞,但是它們必須遵守我們前面描述的域規(guī)則。

          Cookie 的 Secure 屬性

          Secure 屬性是說如果一個 cookie 被設(shè)置了Secure=true,那么這個cookie只能用https協(xié)議發(fā)送給服務(wù)器,用 http 協(xié)議是不發(fā)送的。換句話說,cookie 是在https的情況下創(chuàng)建的,而且他的Secure=true,那么之后你一直用https訪問其他的頁面(比如登錄之后點擊其他子頁面),cookie會被發(fā)送到服務(wù)器,你無需重新登錄就可以跳轉(zhuǎn)到其他頁面。但是如果這時你把url改成http協(xié)議訪問其他頁面,你就需要重新登錄了,因為這個cookie不能在http協(xié)議中發(fā)送。

          可以這樣設(shè)置 Secure 屬性

          response.set_cookie(key="id", value="3db4adj3d", secure=True)
          

          如果要在真實環(huán)境中嘗試,請可以運行以下命令,并注意curl在此處是不通過HTTP保存cookie:

          curl -I http://serene-bastion-01422.herokuapp.com/get-secure-cookie/ --cookie-jar -
          

          相反,通過HTTPS,cookie 出現(xiàn)在cookie jar中:

          curl -I https://serene-bastion-01422.herokuapp.com/get-secure-cookie/ --cookie-jar -
          

          cookie jar 文件:

          serene-bastion-01422.herokuapp.com      FALSE   /       TRUE    0
          

          不要被Secure欺騙:瀏覽器通過HTTPS接受cookie,但是一旦cookie進入瀏覽器,就沒有任何保護。

          因為帶有 Secure 的 Cookie 一般也不用于傳輸敏感數(shù)據(jù).

          Cookie 的 HttpOnly 屬性

          如果cookie中設(shè)置了HttpOnly屬性,那么通過js腳本將無法讀取到cookie信息,這樣能有效的防止XSS攻擊,竊取cookie內(nèi)容,這樣就增加了cookie的安全性,即便是這樣,也不要將重要信息存入cookie。

          XSS 全稱Cross SiteScript,跨站腳本攻擊,是Web程序中常見的漏洞,XSS屬于被動式且用于客戶端的攻擊方式,所以容易被忽略其危害性。其原理是攻擊者向有XSS漏洞的網(wǎng)站中輸入(傳入)惡意的HTML代碼,當(dāng)其它用戶瀏覽該網(wǎng)站時,這段HTML代碼會自動執(zhí)行,從而達到攻擊的目的。如,盜取用戶Cookie、破壞頁面結(jié)構(gòu)、重定向到其它網(wǎng)站等。

          如果有設(shè)置 HttpOnly 看起來是這樣的:

          Set-Cookie: "id=3db4adj3d; HttpOnly"
          

          在 Flask 中

          response.set_cookie(key="id", value="3db4adj3d", httponly=True)
          

          這樣,cookie 設(shè)置了HttpOnly屬性,那么通過js腳本將無法讀取到cookie信息。如果在控制臺中進行檢查,則document.cookie將返回一個空字符串。

          何時使用HttpOnly?cookie 應(yīng)該始終是HttpOnly的,除非有特定的要求將它們暴露給運行時 JS。

          可怕的 SameSite 屬性

          first-party cookie 和 third-party cookie

          查看https://serene-bastion-01422.herokuapp.com/get-cookie/ 中所攜帶的 Cookie

          Set-Cookie: simplecookiename=c00l-c00k13; Path=/
          

          first-party是指你登錄或使用的網(wǎng)站所發(fā)行的 cookie,而third-party cookie 常為一些廣告網(wǎng)站,有侵犯隱私以及安全隱患。

          我們將這類 Cookie 稱為 first-party。也就是說,我在瀏覽器中訪問該URL,并且如果我訪問相同的URL或該站點的另一個路徑(假設(shè)Path為/),則瀏覽器會將cookie發(fā)送回該網(wǎng)站。

          現(xiàn)在考慮在https://serene-bastion-01422.herokuapp.com/get-frog/上的另一個網(wǎng)頁。該頁面設(shè)置了一個cookie,此外,它還從https://www.valentinog.com/cookie-frog.jpg托管的遠程資源中加載圖像。

          該遠程資源又會自行設(shè)置一個cookie:

          我們將這種 cookie 稱為third-party(第三方) Cookie。

          第三方 Cookie 除了用于 CSRF 攻擊,還可以用于用戶追蹤。比如,F(xiàn)acebook 在第三方網(wǎng)站插入一張看不見的圖片。

          <img src="facebook.com" style="visibility:hidden;">
          

          瀏覽器加載上面代碼時,就會向 Facebook 發(fā)出帶有 Cookie 的請求,從而 Facebook 就會知道你是誰,訪問了什么網(wǎng)站。

          使用 SameSite 屬性

          Cookie 的SameSite 屬性用來限制third-party Cookie,從而減少安全風(fēng)險。它可以設(shè)置三個值。

          • Strict
          • Lax
          • None

          Strict最為嚴(yán)格,完全禁止第三方 Cookie,跨站點時,任何情況下都不會發(fā)送 Cookie。換言之,只有當(dāng)前網(wǎng)頁的 URL 與請求目標(biāo)一致,才會帶上 Cookie。

          Set-Cookie: CookieName=CookieValue; SameSite=Strict;
          

          這個規(guī)則過于嚴(yán)格,可能造成非常不好的用戶體驗。比如,當(dāng)前網(wǎng)頁有一個 GitHub 鏈接,用戶點擊跳轉(zhuǎn)就不會帶有 GitHub 的 Cookie,跳轉(zhuǎn)過去總是未登陸狀態(tài)。

          Lax規(guī)則稍稍放寬,大多數(shù)情況也是不發(fā)送第三方 Cookie,但是導(dǎo)航到目標(biāo)網(wǎng)址的 Get 請求除外。

          
          Set-Cookie: CookieName=CookieValue; SameSite=Lax;
          

          導(dǎo)航到目標(biāo)網(wǎng)址的 GET 請求,只包括三種情況:鏈接,預(yù)加載請求,GET 表單。詳見下表。

          設(shè)置了Strict或Lax以后,基本就杜絕了 CSRF 攻擊。當(dāng)然,前提是用戶瀏覽器支持 SameSite 屬性。

          Chrome 計劃將Lax變?yōu)槟J(rèn)設(shè)置。這時,網(wǎng)站可以選擇顯式關(guān)閉SameSite屬性,將其設(shè)為None。不過,前提是必須同時設(shè)置Secure屬性(Cookie 只能通過 HTTPS 協(xié)議發(fā)送),否則無效。

          下面的設(shè)置無效。

          Set-Cookie: widget_session=abc123; SameSite=None
          

          下面的設(shè)置有效。

          Set-Cookie: widget_session=abc123; SameSite=None; Secure
          

          Cookies 和 認(rèn)證

          身份驗證是 web 開發(fā)中最具挑戰(zhàn)性的任務(wù)之一。關(guān)于這個主題似乎有很多困惑,因為JWT中的基于令牌的身份驗證似乎要取代“舊的”、可靠的模式,如基于會話的身份驗證。

          來看看 cookie 在這里扮演什么角色。

          基于會話的身份驗證

          身份驗證是 cookie 最常見的用例之一。

          當(dāng)你訪問一個請求身份驗證的網(wǎng)站時,后端將通過憑據(jù)提交(例如通過表單)在后臺發(fā)送一個Set-Cookie標(biāo)頭到前端。

          型的會話 cookie 如下所示:

          Set-Cookie: sessionid=sty1z3kz11mpqxjv648mqwlx4ginpt6c; expires=Tue, 09 Jun 2020 15:46:52 GMT; HttpOnly; Max-Age=1209600; Path=/; SameSite=Lax
          

          這個Set-Cookie頭中,服務(wù)器可以包括一個名為session、session id或類似的cookie。

          這是瀏覽器可以清楚看到的唯一標(biāo)識符。每當(dāng)通過身份驗證的用戶向后端請求新頁面時,瀏覽器就會發(fā)回會話cookie。

          基于會話的身份驗證是有狀態(tài)的,因為后端必須跟蹤每個用戶的會話。這些會話的存儲可能是:

          • 數(shù)據(jù)庫
          • 像 Redis 這樣的鍵/值存儲
          • 文件系統(tǒng)

          在這三個會話存儲中,Redis 之類應(yīng)優(yōu)先于數(shù)據(jù)庫或文件系統(tǒng)。

          請注意,基于會話的身份驗證與瀏覽器的會話存儲無關(guān)。

          之所以稱為基于會話的會話,是因為用于用戶識別的相關(guān)數(shù)據(jù)存在于后端的會話存儲中,這與瀏覽器的會話存儲不同。

          何時使用基于會話的身份驗證

          只要能使用就使用它。基于會話的身份驗證是一種最簡單、安全、直接的網(wǎng)站身份驗證形式。默認(rèn)情況下,它可以在Django等所有流行的web框架上使用。

          但是,它的狀態(tài)特性也是它的主要缺點,特別是當(dāng)網(wǎng)站是由負載均衡器提供服務(wù)時。在這種情況下,像粘貼會話,或者在集中的Redis存儲上存儲會話這樣的技術(shù)會有所幫助。

          關(guān)于 JWT 的說明

          JWT是 JSON Web Tokens的縮寫,是一種身份驗證機制,近年來越來越流行。

          JWT 非常適合單頁和移動應(yīng)用程序,但它帶來了一系列新挑戰(zhàn)。想要針對API進行身份驗證的前端應(yīng)用程序的典型流程如下:

          • 前端將憑證發(fā)送到后端
          • 后端檢查憑證并發(fā)回令牌
          • 前端在每個后續(xù)請求上帶上該令牌

          這種方法帶來的主要問題是:為了使用戶保持登錄狀態(tài),我將該令牌存儲在前端的哪個地方?

          對于前端開發(fā)來說,最自然的事情是將令牌保存在localStorage中。由于許多原因,這很糟糕。

          localStorage很容易從 JS 代碼訪問,而且它很容易成為XSS攻擊的目標(biāo)。

          為了解決此問題,大多數(shù)開發(fā)人員都將JWT令牌保存在cookie中,以為HttpOnly和Secure可以保護cookie,至少可以免受XSS攻擊。

          將 SameSite 設(shè)置為 strict 就可以完全保護 JWT免受CSRF攻擊

          設(shè)置為SameSite = Strict的新SameSite屬性還將保護你的“熟化” JWT免受CSRF攻擊。但是,由于SameSite = Strict不會在跨域請求上發(fā)送cookie,因此,這也完全使JWT的用例無效。

          那SameSite=Lax呢?此模式允許使用安全的HTTP方法(即GET,HEAD,OPTIONS和TRACE)將 cookie發(fā)送回去。POST 請求不會以任何一種方式傳輸 cookie。

          實際上,將JWT標(biāo)記存儲在cookie或localStorage中都不是好主意。

          如果你確實要使用JWT而不是堅持使用基于會話的身份驗證并擴展會話存儲,則可能要使用帶有刷新令牌的JWT來保持用戶登錄。

          總結(jié)

          自1994年以來,HTTP cookie一直存在,它們無處不在。

          Cookies是簡單的文本字符串,但可以通過Domain和Path對其權(quán)限進行控制,具有Secure的Cookie,只能通過 HTTP S進行傳輸,而可以使用 HttpOnly從 JS隱藏。

          但是,對于所有預(yù)期的用途,cookie都可能使用戶暴露于攻擊和漏洞之中。

          瀏覽器的供應(yīng)商和Internet工程任務(wù)組(Internet Engineering Task Force)年復(fù)一年地致力于提高cookie的安全性,最近的一步是SameSite。

          那么,什么才算是比較安全cookie?,如下幾點:

          • 僅使用 HTTPS
          • 盡可能帶有 HttpOnly 屬性
          • 正確的SameSite配置
          • 不攜帶敏感數(shù)據(jù)

          人才們的 【三連】 就是小智不斷分享的最大動力,如果本篇博客有任何錯誤和建議,歡迎人才們留言,最后,謝謝大家的觀看。


          作者:valentinog 譯者:前端小智 來源:valentinog

          原文:https://gizmodo.com/the-complete-guide-to-cookies-and-all-the-stuff-w-1794247382


          主站蜘蛛池模板: 亚洲国产专区一区| 日韩精品久久一区二区三区| 精品国产一区二区三区AV| 中日韩精品无码一区二区三区| 亚洲一区二区三区高清不卡 | 日本一道高清一区二区三区| 中文字幕精品一区二区| 久久久久久免费一区二区三区| 爆乳无码AV一区二区三区| 精品欧洲av无码一区二区 | 亚洲午夜精品一区二区| 亚洲视频一区调教| 国产视频一区在线观看| 91国在线啪精品一区| 狠狠爱无码一区二区三区| 欧美亚洲精品一区二区| 亚洲日韩AV一区二区三区中文| 国产无人区一区二区三区| 2021国产精品视频一区| 国产一区二区三区播放心情潘金莲| 亚洲国产激情在线一区| 国产欧美一区二区精品仙草咪| 无码精品人妻一区二区三区AV| 亚洲国产成人久久一区久久| 日本不卡在线一区二区三区视频| 国产在线无码视频一区二区三区| 99精品一区二区三区| 精品国产AV无码一区二区三区| 无码少妇一区二区浪潮av| 无码精品人妻一区二区三区中 | 国产av一区最新精品| 2018高清国产一区二区三区| 爆乳无码AV一区二区三区 | 国内精品一区二区三区在线观看| 久久er99热精品一区二区| 无码AV天堂一区二区三区| 国产精品无圣光一区二区| 日韩高清国产一区在线| 怡红院一区二区三区| 国产精品无圣光一区二区| 亚洲一区爱区精品无码|