好好了解一下Cookie


Cookie的誕生

由於HTTP協議是無狀態的,而服務器端的業務必須是要有狀態的。Cookie誕生的最初目的是為了存儲web中的狀態信息,以方便服務器端使用。比如判斷用戶是否是第一次訪問網站。目前最新的規范是RFC 6265,它是一個由瀏覽器服務器共同協作實現的規范。
Cookie的處理分為:

  • 服務器像客戶端發送cookie
  • 瀏覽器將cookie保存
  • 之后每次http請求瀏覽器都會將cookie發送給服務器端

服務器端的發送與解析

發送cookie

服務器端像客戶端發送Cookie是通過HTTP響應報文實現的,在Set-Cookie中設置需要像客戶端發送的cookie,cookie格式如下:

Set-Cookie: "name=value;domain=.domain.com;path=/;expires=Sat, 11 Jun 2016 11:29:42 GMT;HttpOnly;secure"

其中name=value是必選項,其它都是可選項。Cookie的主要構成如下:

  • name:一個唯一確定的cookie名稱。通常來講cookie的名稱是不區分大小寫的。
  • value:存儲在cookie中的字符串值。最好為cookie的name和value進行url編碼
  • domain:cookie對於哪個域是有效的。所有向該域發送的請求中都會包含這個cookie信息。這個值可以包含子域(如:yq.aliyun.com),也可以不包含它(如:.aliyun.com,則對於aliyun.com的所有子域都有效).
  • path: 表示這個cookie影響到的路徑,瀏覽器跟會根據這項配置,像指定域中匹配的路徑發送cookie。
  • expires:失效時間,表示cookie何時應該被刪除的時間戳(也就是,何時應該停止向服務器發送這個cookie)。如果不設置這個時間戳,瀏覽器會在頁面關閉時即將刪除所有cookie;不過也可以自己設置刪除時間。這個值是GMT時間格式,如果客戶端和服務器端時間不一致,使用expires就會存在偏差
  • max-age: 與expires作用相同,用來告訴瀏覽器此cookie多久過期(單位是秒),而不是一個固定的時間點。正常情況下,max-age的優先級高於expires。
  • HttpOnly: 告知瀏覽器不允許通過腳本document.cookie去更改這個值,同樣這個值在document.cookie中也不可見。但在http請求張仍然會攜帶這個cookie。注意這個值雖然在腳本中不可獲取,但仍然在瀏覽器安裝目錄中以文件形式存在。這項設置通常在服務器端設置。
  • secure: 安全標志,指定后,只有在使用SSL鏈接時候才能發送到服務器,如果是http鏈接則不會傳遞該信息。就算設置了secure 屬性也並不代表他人不能看到你機器本地保存的 cookie 信息,所以不要把重要信息放cookie就對了
    服務器端設置cookie示例如下:
var http = require('http');
var fs = require('fs');

http.createServer(function(req, res) {
	res.setHeader('status', '200 OK');
	res.setHeader('Set-Cookie', 'isVisit=true;domain=.yourdomain.com;path=/;max-age=1000');
	res.write('Hello World');
	res.end();
}).listen(8888);

console.log('running localhost:8888')

5BY_CCV_Q_VJ1__2_TV1
O6PXV8YEU5EJ0G____Q20E5
直接設置Set-Cookie過於原始,我們可以對cookie的設置過程做如下封裝:

var serilize = function(name, val, options) {
	if (!name) {
		throw new Error("coolie must have name");
	}
	var enc = encodeURIComponent;
	var parts = [];
	
	val = (val !== null && val !== undefined) ? val.toString() : "";
	options = options || {};
	parts.push(enc(name) + "=" + enc(val));
	// domain中必須包含兩個點號
	if (options.domain) {
		parts.push("domain=" + options.domain);
	}
	if (options.path) {
		parts.push("path=" + options.path);
	}
	// 如果不設置expires和max-age瀏覽器會在頁面關閉時清空cookie
	if (options.expires) {
		parts.push("expires=" + options.expires.toGMTString());
	}
	if (options.maxAge && typeof options.maxAge === "number") {
		parts.push("max-age=" + options.maxAge);
	}
	if (options.httpOnly) {
		parts.push("HTTPOnly");
	}
	if (options.secure) {
		parts.push("secure");
	}
	
	return parts.join(";");
}

需要注意的是,如果給cookie設置一個過去的時間,瀏覽器會立即刪除該cookie;此外domain項必須有兩個點,因此不能設置為localhost:

something that wasn't made clear to me here and totally confused me for a while was that domain names must contain at least two dots (.),hence 'localhost' is invalid and the browser will refuse to set the cookie!

服務器端解析cookie

cookie可以設置不同的域與路徑,所以對於同一個name value,在不同域不同路徑下是可以重復的,瀏覽器會按照與當前請求url或頁面地址最佳匹配的順序來排定先后順序
X_W6SLE3H_AUV181U6D_Q_H
所以當前端傳遞到服務器端的cookie有多個重復name value時,我們只需要最匹配的那個,也就是第一個。服務器端解析代碼如下:

var parse = function(cstr) {
	if (!cstr) {
		return null;
	}
	
	var dec = decodeURIComponent;
	var cookies = {};
	var parts = cstr.split(/\s*;\s*/g);
	parts.forEach(function(p){
		var pos = p.indexOf('=');
		// name 與value存入cookie之前,必須經過編碼
		var name = pos > -1 ? dec(p.substr(0, pos)) : p;
		var val = pos > -1 ? dec(p.substr(pos + 1)) : null;
		//只需要拿到最匹配的那個
		if (!cookies.hasOwnProperty(name)) {
			cookies[name] = val;
		}/* else if (!cookies[name] instanceof Array) {
			cookies[name] = [cookies[name]].push(val);
		} else {
			cookies[name].push(val);
		}*/
	});
	
	return cookies;
}

客戶端的存取

瀏覽器將后台傳遞過來的cookie進行管理,並且允許開發者在JavaScript中使用document.cookie來存取cookie。但是這個接口使用起來非常蹩腳。它會因為使用它的方式不同而表現出不同的行為

  • 當用來獲取屬性值時,document.cookie返回當前頁面可用的(根據cookie的域、路徑、失效時間和安全設置)所有的字符串,字符串的格式如下:
"name1=value1;name2=value2;name3=value3";
  • 當用來設置值的時候,document.cookie屬性可設置為一個新的cookie字符串。這個字符串會被解釋並添加到現有的cookie集合中。如:
document.cookie = "_fa=aaaffffasdsf;domain=.dojotoolkit.org;path=/"

設置document.cookie並不會覆蓋cookie,除非設置的name value domain path都與一個已存在cookie重復。

由於cookie的讀寫非常不方便,我們可以自己封裝一些函數來處理cookie,主要是針對cookie的添加、修改、刪除。

var cookieUtils = {
        get: function(name){
          var cookieName=encodeURIComponent(name) + "=";
          //只取得最匹配的name,value
          var cookieStart = document.cookie.indexOf(cookieName);
          var cookieValue = null;
          
          if (cookieStart > -1) {
            // 從cookieStart算起
            var cookieEnd = document.cookie.indexOf(';', cookieStart);
            //從=后面開始
            if (cookieEnd > -1) {
              cookieValue = decodeURIComponent(document.cookie.substring(cookieStart + cookieName.length, cookieEnd));
            } else {
              cookieValue = decodeURIComponent(document.cookie.substring(cookieStart + cookieName.length, document.cookie.length));
            }
          }
          
          return cookieValue;
        },
        
        set: function(name, val, options) {
        	if (!name) {
        		throw new Error("coolie must have name");
        	}
        	var enc = encodeURIComponent;
        	var parts = [];
        	
        	val = (val !== null && val !== undefined) ? val.toString() : "";
        	options = options || {};
        	parts.push(enc(name) + "=" + enc(val));
        	// domain中必須包含兩個點號
        	if (options.domain) {
        		parts.push("domain=" + options.domain);
        	}
        	if (options.path) {
        		parts.push("path=" + options.path);
        	}
        	// 如果不設置expires和max-age瀏覽器會在頁面關閉時清空cookie
        	if (options.expires) {
        		parts.push("expires=" + options.expires.toGMTString());
        	}
        	if (options.maxAge && typeof options.maxAge === "number") {
        		parts.push("max-age=" + options.maxAge);
        	}
        	if (options.httpOnly) {
        		parts.push("HTTPOnly");
        	}
        	if (options.secure) {
        		parts.push("secure");
        	}
        	
        	document.cookie = parts.join(";");
        },
        delete: function(name, options) {
          options.expires = new Date(0);// 設置為過去日期
          this.set(name, null, options);
        }
      }


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM