Cookie詳解

zcl_love_wx發表於2016-07-25

在這裡插入圖片描述

cookie簡介

1. 定義

cookie是由伺服器傳送給客戶端(瀏覽器)的小量資訊。

2. 作用

cookie是鍵值對形式儲存的少量資訊,那它有什麼作用呢?

我們知道,平時上網時都是使用無狀態的HTTP協議傳輸出資料,這意味著客戶端與服務端在資料傳送完成後就會中斷連線。這時我們就需要一個一直保持會話連線的機制。在session出現前,cookie就完全充當了這種角色。也就是,cookie的小量資訊能幫助我們跟蹤會話。一般該資訊記錄使用者身份。

當然cookie也常記錄跟蹤購物車的商品資訊(如數量)、記錄使用者訪問次數等。

3. 原理

客戶端請求伺服器時,如果伺服器需要記錄該使用者狀態,就使用response向客戶端瀏覽器頒發一個Cookie。而客戶端瀏覽器會把Cookie儲存起來。當瀏覽器再請求伺服器時,瀏覽器把請求的網址連同該Cookie一同提交給伺服器。伺服器通過檢查該Cookie來獲取使用者狀態。

4.新增cookie示例

獲取客戶端的Cookie時,只能獲取name與value屬性,其它屬性都不會被提交。

Cookie c = new Cookie("username","peter");// 新建一個Cookie物件
c.setMaxAge(24*60*60);              	  // 設定過期時間1天,以秒為單位
response.addCookie(c);              	  // 儲存cookie到客戶端

5.刪除cookie示例

刪除某個Cookie時,只需要新建一個只有maxAge和value不一樣的同名Cookie,然後新增到response中覆蓋原來的Cookie

Cookie cookie = new Cookie("username","peter");// 新建Cookie
cookie.setMaxAge(0);                           // 設定生命週期為0,表示將要刪除
response.addCookie(cookie);                    // 執行新增後就從response裡刪除了

6.修改cookie示例

修改某個Cookie時,只需要新建一個只有value屬性不一樣的同名Cookie,然後新增到response中覆蓋原來的Cookie

Cookie cookie = new Cookie("username","joker");// 新建Cookie
cookie.setMaxAge(24*60*60);                    // 設定生命週期
response.addCookie(cookie);                    // 執行新增後就從response裡覆蓋修改了

注意:修改、刪除Cookie時,新建的Cookie除value、maxAge之外的所有屬性,例如name、path、domain等,都要與原Cookie完全一樣。否則,瀏覽器將視為兩個不同的Cookie而不會覆蓋之前的Cookie,從而導致修改、刪除失敗。

Cookie類的各方法詳解

Cookie類在javax.servlet.http.Cookie包中

方法名 返回型別 說明
setValue(String newValue) void 給當前cookie重新賦值
setComment(String purpose) void 對該cookie進行描述的資訊(說明作用),瀏覽器顯示cookie資訊時能看到
setDomain(String pattern) void 符合該pattern(域名正則)的就可以訪問該Cookie的域名。如果設定為“.google.com”,則所有以“google.com”結尾的域名都可以訪問該Cookie。注意第一個字元必須為“.”
setHttpOnly(boolean httpOnly) void 設為true後,只能通過http訪問,javascript無法訪問,還可防止xss讀取cookie
setMaxAge(int expiry) void 該Cookie失效時間,單位秒。如果為正數,則該Cookie在expiry秒之後失效。如果為負數,該Cookie為臨時Cookie,關閉瀏覽器即失效,瀏覽器也不會以任何形式儲存該Cookie。如果為0,表示刪除該Cookie。預設為–1
setPath(String uri) void 設定Cookie的使用路徑。後面緊接著詳解。如果設定為“/agx/”,則只有uri為“/agx”的程式可以訪問該Cookie。如果設定為“/”,則本域名下的uri都可以訪問該Cookie。注意最後一個字元必須為"/"
setSecure(boolean flag) void 是否使用安全傳輸協議。為true時,只有當是https請求連線時cookie才會傳送給伺服器端,而http時不會,但是服務端還是可以傳送給瀏覽端的。

對應的getter方法我就不講了。
如果cookie值為Unicode字元,需要為字元編碼。如果cookie值為二進位制資料,則需要使用BASE64編碼

cookie的setPath()和setDomain()方法

預設情況下cookie只能在一個應用中共享,即一個cookie只能由建立它的應用獲得。

設定同一伺服器內的cookie使用範圍用setPath

  1. c.setPath("/"),使同一伺服器內所有應用都可訪問到該Cookie:
Cookie c = new Cookie("name","peter");
c.setMaxAge(24*60*60);  
c.setPath("/");//同一伺服器內所有應用都可訪問到該Cookie
response.addCookie(c); //儲存cookie到客戶端

  1. 如果同一伺服器內有兩個應用agx1.0和agx2.0
    當我們在agx1.0裡有c.setPath("/agx2.0/");時,該cookie就只能在agx2.0下面能獲取到,就連建立該cookie的agx1.0也獲取不到。

  2. 如果在agx1.0裡有c.setPath("/agx2.0/abc/");時,則只能在agx2.0/abc下面才能獲得該cookie,即使在agx2.0下面也不能獲取到。
    ##cookie的跨域
    1.正常情況下Cookie不可跨域名,如域名www.google.com頒發的Cookie不會被提交到域名www.baidu.com去。這是由Cookie的隱私安全機制決定的。即使在同一個一級域名下的兩個二級域名如www.agx.com和images.agx.com也不能互動使用Cookie,因為二者的域名並不嚴格相同。如果想所有agx.com名下的二級域名都可以使用該Cookie,則需要設定Cookie的domain引數為".agx.com",例如:

Cookie cookie = new Cookie("name","peter"); // 新建Cookie
cookie.setDomain(".agx.com");          		// 設定域名
cookie.setPath("/");                        // 設定路徑
cookie.setMaxAge(Integer.MAX_VALUE);        // 設定有效期為永久
response.addCookie(cookie);                 // 輸出到客戶端

讀者可以修改本機C:\WINDOWS\system32\drivers\etc下的hosts檔案來配置多個臨時域名,然後使用setCookie.jsp程式來設定跨域名Cookie驗證domain屬性。
注意:domain引數必須以點(".")開始。另外,name相同但domain不同的兩個Cookie是兩個不同的Cookie。如果想要兩個域名完全不同的網站共有Cookie,可以生成兩個name相同的Cookie,domain屬性分別為兩個域名。

2.若A伺服器的域名為:adv.audiogroup.com,有應用名為:agx1.0; B伺服器的域名為:agx.com,有應用名為:agx2.0。
在A伺服器的agx1.0應用下設定cookie如下:

Cookie cookie = new Cookie("name","peter"); // 新建Cookie
cookie.setDomain(".agx.com");          		// 設定域名
cookie.setPath("/");                        // 設定路徑
cookie.setMaxAge(Integer.MAX_VALUE);        // 設定有效期為永久
response.addCookie(cookie);                 // 輸出到客戶端

這時,在B伺服器下的agx2.0應用和agx1.0裡都能取到上面的Cookie。
注:輸入URL訪問agx2.0時,必須輸入域名才能獲取其它伺服器共享給它的cookie,如:
輸入http://images.agx.com:8080/agx2.0,可以獲取agx1.0在客戶端設定的cookie
輸入:http://localhost:8080/agx2.0則不可以獲得cookie。

setPath()與setDomain()的區別?

setDomain()主要用來確定兩個不同名稱但字尾相同的網站地址是否能使用同一個Cookie。
例: www.agx.com和 bbs.agx.com只要有cookie.setDomain(".agx.com");就都能使用該cookie

setPath()主要用來確定地址裡什麼字尾下能夠使用這個Cookie

歸結起來就是:setDomain決定允許訪問Cookie的域名,而setPath決定允許訪問Cookie的路徑(ContextPath)

#獲取使用者請求裡的cookie

Cookie[] cookie = request.getCookies();//獲取的是請求裡的所有cookie組成的陣列
for(int i=0;i<cookie.length;i++){
	if("name".equals(cookie[i].getName())){
		System.out.println(cookie[i].getValue());//得到peter
		break;
	}
}

解決cookie裡中文亂碼問題

Cookie中要儲存中文只能編碼。一般使用UTF-8編碼即可。不推薦使用GBK等中文編碼,因為瀏覽器不一定支援,而且JavaScript也不支援GBK編碼。

Cookie不僅可以使用ASCII字元與Unicode字元,還可以使用二進位制資料。例如在Cookie中使用數字證照,提供安全度。使用二進位制資料時也需要進行編碼(如用BASE64編碼儲存二進位制圖片)。由於瀏覽器每次請求都會帶著Cookie,因此Cookie內容不宜過多,所以一般不會在Cookie中儲存二進位制的內容

JS操作Cookie

Cookie是儲存在客戶端的,所以瀏覽器可以使用指令碼(JS)等操作Cookie。

<script language=javascript> 
	//新增cookie
	function setCookie(name,value,time){ 
		var date= new Date(); 
		date.setDate(date.getDate()+time); 
		document.cookie = name+"="+value+";expires="+date; 
	} 
	
	//獲得cookie
	function getCookie(name){ 
		var arr = document.cookie.split(";"); 
		for(var i=0; i<arr.length; i++){ 
		var arr2 = arr[i].split("="); 
			if(arr2[0] == name){ 
				return arr2[1]; 
			} 
		} 
		return null; 
	} 
	
	//刪除cookie
	function removeCookie(name){ 
		setCookie(name,"",0) 
	} 
</script>
W3C標準的瀏覽器會阻止JavaScript讀寫任何不屬於自己網站的Cookie。即A網站的JavaScript程式碼裡獲取不到B網站的Cookie。

使用cookie記住密碼

方案1
直接把使用者名稱與密碼都保持到Cookie中,下次訪問時檢查Cookie中的使用者名稱與密碼,與資料庫比較。這是一種比較危險的選擇,一般不把密碼等重要資訊儲存到Cookie中。
方案2
把密碼加密後儲存到Cookie中,下次訪問時解密並與資料庫比較。這種方案略微安全一些。如果不希望儲存密碼,還可以把登入的時間戳儲存到Cookie與資料庫中,到時只驗證使用者名稱與登入時間戳就可以了。
方案3
實現方式是把賬號按照一定的規則(金鑰)加密後,連同賬號一塊儲存到Cookie中。下次訪問時只需要判斷賬號的加密規則是否相同即可。
加 密:

String userName = request.getParameter("userName");//獲取使用者名稱
Md5Hash psw = new Md5Hash(userName, "peter.com", 2);//以peter.com為金鑰加密

//將使用者名稱新增進cookie併傳送給客戶端
Cookie userCookie = new Cookie("userName",userName);
userCookie.setMaxAge(7*24*60*60);
response.addCookie(userCookie);

//將金鑰生成的密文加進cookie併傳送給客戶端
Cookie c = new Cookie("key",psw.toString());
c.setMaxAge(7*24*60*60);
response.addCookie(c);

解密:

String usreName = null;
String key = null;
Cookie[] cookie = request.getCookies();
if(cookie !=null){              
    for(Cookie c:cookie){  // 遍歷Cookie
       if("userName".equals(c.getName()) 
           userName = c.getValue();      
       if("key".equals(c.getName()) 
           key= cookie.getValue();       
    }
}
if(userName != null && key != null){   
	String secrect = new Md5Hash(userName, "peter.com", 2);
	if(key.equals(secrect )){//加密規則正確,說明已經登入
		//...
		//....省略後續處理
	}
}

由上面可知,如果將密碼加密後存進cookie後,當我們下次訪問登入頁時,密碼框裡的密碼將不是使用者真正的密碼。

相關文章