經過Cookie的快速入門與代碼使用。如果想深入理解Cookie的技術實現,就得去理解它的原理。
且有些時候使用Cookie,還要根據需求設置存活期限以及確定Cookie獲取范圍等其他細節。最后,我們會總結Cookie這門客戶端會話技術的作用。
一、原理
注:我們還是使用簡易圖畫進行輔助理解
注:這是上一節寫的代碼(兩個Servlet),這里將這個例子使用一下分析Cookie的原理
所謂分析Cookie原理,正是分析: 服務器端如何響應時發送Cookie,以至于瀏覽器端識別了還自動保存;并且CookieDemo2怎么獲取到的Cookie,即瀏覽器怎么就攜帶了Cookie信息進行了下一次請求。
1.1 分析:
?其實不管發送數據也好,還是獲取數據(站在服務器端看)也好,它們在客戶端與我端(服務端)中間都是以HTTP協議進行交互——響應和請求一定是遵循HTTP協議的。
首先,客戶端發送了第一次請求,請求訪問了服務器端的資源——CookieDemo1。
CookieDemo1收到了請求,于是以“發送Cookie”進行處理數據并響應了(確實向瀏覽器發送了Cookie),剛剛說“所有的客戶與服務器端的響應請求都遵循HTTP協議”。所以那實際上,“發送Cookie”時,怎么發送呢? 服務器(response端)會在響應報文中設置一個名為“Set-Cookie”的響應頭,該響應頭的值以鍵值對的形式呈現,恰好可用于存儲Cookie
于是瀏覽器端會收到來自服務器端的響應報文,發現響應報文的響應頭有“set-Cookie”。瀏覽器因為熟讀HTTP協議,所以遵循協議:如果瀏覽器收到了set-Cookie響應頭,它應該將這個頭里攜帶的數據(“name=Bear”)保存到自己本地中。
而在第二次請求時,HTTP協議又規定了:瀏覽器應該在下一次請求中將保存的數據(“name=Bear”)放進一個Cookie的請求頭中(作為值)。瀏覽器端會在請求報文中設置一個Cookie請求頭,其值類型也為鍵值對,恰好也用來放Cookie數據。
?于是CookieDemo2能從請求頭中獲取到Cookie信息。但是,在咱們使用Cookie代碼獲取請求里的Cookie,并不是操作所謂的“Cookie這個請求頭”,而是怎么??
Cookie[] cookies = request.getCookies();
//這樣一個名叫getCookies()的方法
?這也得多虧了JAVA EE 內部實現的API,封裝的庫方法(函數)。讓我們調用庫里的方法就能發送和獲取Cookie,而方法便是去和請求頭或者響應頭打交道,我們放心調用就行。
1.2 看看:
初見"set-Cookie"響應頭和“Cookie”請求頭,不熟悉很正常,多看幾眼。我們在瀏覽器抓包
1.2.1抓包
(瀏覽器端)按F12鍵 ,接著如圖操作:
我們現在運行,訪問CookieDemo1—— :抓抓它的響應包:?
是吧,這發送Cookie響應時會真的有一個響應標頭Set-Cookie。
那如果我們繼續請求CookieDemo2:抓抓它的請求報文,是不是在存儲Cookie后下一次請求就會在請求頭里捎帶上Cookie數據。
確實是。?
?二、Cookie的細節
2.1?一次可不可以發送多個cookie呢?
了解了原理這個問題== 響應報文set-Cookie的值可以是多個鍵值對嗎??
我們修改修改CookieDemo1:
// 原
@WebServlet("/CookieServlet01")
public class CookieServlet01 extends HttpServlet {protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {doPost(request,response);}protected void doPost(HttpServletRequest request,HttpServletResponse response) throws ServletException,IOException{// 1. 創建Cookie,綁定數據Cookie cookie = new Cookie("name","Bear");// 2. 發送Cookie對象response.addCookie(cookie);}}
我們再創建一個Cookie,讓response調用兩次addCookie();
protected void doPost(HttpServletRequest request,HttpServletResponse response) throws ServletException,IOException{// 1. 創建Cookie,綁定數據Cookie cookie = new Cookie("name","Bear");Cookie cookie2 = new Cookie("place","Toilet");//新建// 2. 發送Cookie對象response.addCookie(cookie);response.addCookie(cookie2);//連著發送}
運行這段代碼,即重新請求CookieDemo1,抓抓它的請求包:
答案:一次可以發送多個Cookie。
為了完整,我們再訪問打印Cookie的那個Servlet,抓抓這回的請求包:
都存了,是的,剛剛響應發來的Cookie都存進瀏覽器了;這會兒,也全部放進請求頭Cookie了。
2.2 Cookie在瀏覽器中能保存多長時間?
默認情況下,當瀏覽器關閉后,Cookie數據被銷毀;因為默認存到瀏覽器內存里,程序結束,內存就會刪除數據。
如果想設置持久化存儲,我們這里再學習一個Cookie的方法:
cookie.setMaxAge(int seconds)
//參數是int類型,單位為秒數。
// int 是有符號整型,于是根據取值的不同,該方法會實現不同的效果
?若seconds 為 正數 :意味著 a.?將Cookie數據寫到硬盤的文件中、持久化存儲。
b. 規定了該Cookie存活的時間
若seconds 為 0 : 意味著刪除該Cookie。設置完cookie的MaxAge,需要將這個Cookie發送給客戶端。
為什么秒數為0,就意味著刪除? 這句話看似只有1個問,其實有2個答案。
1. Cookie 最大存活期限為0,那么就意味著“不存在”。
2. Cookie是存在用戶端的數據,服務器端不能直接操作客戶端的數據,所以服務器想要刪除Cookie就用這種方式:設MaxAge=0,再響應發送。
若seconds為 負數:意味著默認設置(所以設為負數,跟不設置MaxAge效果一樣:一旦關閉瀏覽器,存在內存里的Cookie就會被釋放,失效)
代碼實踐timing :
我們新建一個CookieServlet03來創建和發送Cookie。
/** CookieDemo03 用來創建和發送Cookie* 分別設置存活期限:* ^ 正數* ^ 負數* ^ 0* */
@WebServlet("/CookieServlet03")
public class CookieServlet03 extends HttpServlet {protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {doPost(request,response);}protected void doPost(HttpServletRequest request,HttpServletResponse response) throws ServletException,IOException{// 1. 創建Cookie,綁定數據// 2. 設置Cookie的存活期限// 3. 發送Cookie對象}
}
?寫入核心代碼:
// 1. 創建Cookie,綁定數據Cookie cookie = new Cookie("name","bear");// 2. 設置Cookie的存活期限cookie.setMaxAge(30);//設置正數的存活期限,代表了:1.將Cookie數據寫到硬盤的文件中。持久化存儲。2.Cookie存活時間// 3. 發送Cookie對象response.addCookie(cookie);
?驗證邏輯:我們重新打開瀏覽器,先訪問CookieServlet03(客戶端收到cookie會存起來),再請求訪問CookieDemo02(這時可在Eclipse控制臺打印出Cookie信息)。
如果在30秒之內,我們關閉瀏覽器又訪問CookieServlet02,按理可以獲取并打印這個Cookie:
如果超過30秒后,我們再次訪問CookieServlet02,我們再看控制臺:
負數和0的取值,也是同樣驗證步驟。
?2.3?cookie能不能存中文?
在tomcat 8之前,不能存中文。
那怎么辦? 需要將中文數據轉碼 —— 一般采用URL編碼(如:%E3)
在tomcat 8之后 ,cookie支持中文數據,但還是不支持特殊字符。
我們現在使用的是tomcat 9,故tomcat8之前的不做演示。
運行服務器,訪問CookieServlet03,再訪問CookieServlet02打印:
以上驗證,tomcat8以上,Cookie能存中文。
2.4 cookie獲取范圍多大?
假設在一個服務器(也就是一個tomcat)中,部署了多個web項目,那么在這些web項目中cookie能不能共享?

經過作者的超絕不經意安排,有以下兩個項目:?
?它們的cookieDemo2和cookieServlet02都是獲取request的Cookies并打印。
這時我們驗證的問題:項目1下的cookieServlet01發送給客戶端的Cookie,會被項目2下的cookieDemo2獲取到嗎(即跨項目的cookie能獲得嗎)

我們接下來就訪問這個Servlet:
接著我們訪問項目2下的cookieDemo2:?
?最后得到驗證:
假設在一個服務器中,部署了多個web項目,那么在這些web項目中cookie能不能共享?
默認情況下cookie不能共享
假如就有這個需求,需要一個服務器上多個項目共享Cookie,該如何呢?
2.4.2 多個項目共享Cookie
之所以Cookie多個項目不能共享,是因為默認情況下,客戶端存儲的Cookie目錄是以項目名為目錄的:
項目不同,項目名就不同。自然該目錄下就不會出現其他項目名的Cookie。
這里引入Cookie的另一個方法:
Cookie.setPath(String Path);
//參數為字符串類型,表示該cookie的存儲路徑。
?運行:
?此時若我們再訪問另一個項目下的打印Servlet:
?
再看控制臺,成功打印了項目1剛剛存的Cookie。
所以,想要實現一個服務器下多個項目下的Cookie共享:就setPath()。
2.4.3 多個服務器Cookie共享
其實還是同樣的道理,調用cookie方法,但是是不同的方法:
setDomain(String path)
// 參數:字符串類型,設置域名
如果設置一級域名相同,那么多個服務器之間的cookie可以共享
如果SetDomain(“.baidu.com”),那么tieba.baidu.com和news.baidu.com中cookie可以共享
因為這兩個服務器都是baidu管轄下的,那何必要設置兩個服務器呢?是因為如果一個服務器承受不了大量的用戶請求訪問。
比較淺顯的解釋,但這里簡單提出,僅為知識體系的完整。后面再次接觸的時候就會深入應用。
三、Cookie的特點和作用:
特點:
1.?? ?cookie存儲數據在客戶端瀏覽器
2.?? ?瀏覽器對于單個cookie的大小有限制(4kb左右) 以及 對于同一個域名下的總cookie數量也有限制(20個)
這Cookie的名字也暗示了存不了多少數據。Cookie——“小甜點”,飯后甜食或者下午茶小點心,不能當主食。
?作用:
1.?? ?Cookie一般用于存儲少量的不太敏感的數據
2.?? ?在不登錄的情況下,完成服務器對客戶端的身份識別;而登錄的話,就是在數據庫里獲取數據
“不太敏感”,存在客戶端,咱們一抓包或者在“開發者工具”里就能看到,服務器這邊還能刪除……無不昭示著客戶端安全性不太高,盡量不存敏感數據。
?第二點還挺重要,需要好好理解:
我們來到baidu的主頁面:
我們在輸入框輸入問題:
?如果我們既不登錄,又想實現輸入問題的時候沒有自動聯想:
我們再次來到主頁面,此時還是未登錄狀態:
若我關閉瀏覽器,再次進入baidu,輸入問題也沒有自動聯想。為什么呢?是因為剛剛的“保存設置”時發出請求,baidu響應了這些設置,數據以set-Cookie標頭響應過來。
瀏覽器保存這些信息(Cookie),而我們再次來到baidu主頁面:我們的請求會自動攜帶Cookie。而baidu服務器則會獲取請求里的Cookie,再將我們的需求響應給我們。
這就是Cookie:在未登錄的狀態下?,服務器完成對客戶端身份的識別。如果登錄的話,服務器就直接在它的數據庫里提取數據了。
下一節:Cookie的實戰應用