????????在 Web 開發中,會話跟蹤是一個核心問題。HTTP 協議是無狀態的,這意味著服務器無法直接記住客戶端的狀態。而 Cookie 和 Session 技術的出現,正是為了解決這一難題。
一、Cookie概述
????????Cookie,翻譯成中文是小甜點、小餅干的意思。在 HTTP 協議中,它代表著服務器送給客戶端瀏覽器的 “小甜點”。實際上,Cookie 就是由一個鍵和一個值構成的信息,會隨著服務器端的響應發送給客戶端瀏覽器。客戶端瀏覽器會將其保存起來,等下一次訪問該服務器時,再把 Cookie 發送回服務器。
Cookie 的規范
不用擔心 Cookie 會占滿你的硬盤,因為它有嚴格的規范限制:
- 一個 Cookie 最多只有 4KB 大小。
- 瀏覽器最多可以保存 300 個 Cookie。
????????不過,在瀏覽器競爭的大環境下,一些瀏覽器對 Cookie 規范進行了 “擴展”,比如有的瀏覽器允許每個 Cookie 為 8KB,最多可保存 500 個 Cookie,但即便如此,也不會出現占滿硬盤的情況。另外,不同的瀏覽器之間是不能共享 Cookie 的。
Cookie 的作用
????????Cookie 的核心作用就是 “跟蹤客戶端狀態”。服務器把信息保存在客戶端,客戶端下次請求時再把這些信息還給服務器,服務器就能通過這些信息識別客戶端了。
Cookie 的基本操作
- 保存 Cookie 到客戶端:這是響應工作的一部分,通過 HttpServletResponse 類的
void addCookie(Cookie c)
方法來實現,該方法可多次調用以添加多個 Cookie。例如:
Cookie cookie = new Cookie("username", "txjava");
response.addCookie(cookie);
????????這樣,在響應頭中就會添加 Set - Cookie 的值,客戶端瀏覽器也會保存該 Cookie。當下一次訪問服務器時,請求頭中就會帶著這個 Cookie 的值。
- 服務器端讀取 Cookie:瀏覽器保存 Cookie 后,會在下次請求時把 Cookie 放到請求頭中發送給服務器,服務器可通過 HttpServletRequest 的
Cookie[] getCookies()
方法讀取。示例如下:
Cookie[] cookies = request.getCookies();
if(cookies != null){for(Cookie cookie : cookies){System.out.println(cookie.getName() +":"+ cookie.getValue());}
}
Cookie 的生命周期
????????Cookie 的存活時間取決于其生命周期設置,默認情況下,Cookie 只在瀏覽器內存中存活,關閉瀏覽器后就會消失。可通過Cookie#setMaxAge(int expiry)
方法設置存活時間(參數為秒數):
cookie.setMaxAge(60*60*24*30*12)
:表示 Cookie 可存活 1 小時(此處原文檔描述有誤,60*60 才是 1 小時),此時瀏覽器不僅會把 Cookie 保存在內存中,還會保存到硬盤上,即便關閉瀏覽器、重啟電腦,Cookie 也會存在 1 小時。cookie.setMaxAge(-1)
:這是默認值(其實只要是負數都一樣),表示 Cookie 只在瀏覽器內存中存活,關閉瀏覽器窗口后就會消失。cookie.setMaxAge(0)
:表示 Cookie 被作廢,既不在內存中存活,也不在硬盤上存活,目的是覆蓋客戶端原來的該 Cookie 使其失效。
Cookie 的路徑
????????Cookie 有一個 path 屬性,可通過Cookie#setPath(String)
方法設置。若不設置,其默認路徑就是請求的路徑。比如請求http://localhost:8080/cookie_demo/path
時,服務器響應的 Cookie 默認路徑是/cookie_demo
;請求http://localhost:8080/cookie_demo/path/son
時,默認路徑是/cookie_demo/path
。
Cookie 路徑的作用是決定服務器的請求是否會從瀏覽器中加載某些 Cookie。例如,有兩個 Cookie:
- cookie1:name=path1;value=pathvalue1;path=/cookie_demo
- cookie2:name=path1;value=pathvalue2;path=/cookie_demo/path
????????當訪問http://localhost:8080/cookie_demo/*
時,請求頭中會包含 cookie1,不包含 cookie2;當訪問http://localhost:8080/cookie_demo/path/*
時,請求頭中會包含 cookie1 和 cookie2。也就是說,訪問子路徑時會包含父路徑的 Cookie,訪問父路徑時不包含子路徑的 Cookie。
????????若想在 BServlet 中設置的 Cookie,在客戶端訪問 AServlet 時也能包含在請求頭中,可設置 BServlet 中 Cookie 的 path,如c2.setPath(“/cookie_demo”)
(硬編碼)或c2.setPath(request.getContextPath() + “/”)
(活編碼)。
Cookie 保存中文
????????Cookie 中不能直接設置中文,但可先使用URLEncoder.encode()
方法編碼后再存放,獲取時先使用URLDecoder.decode()
方法解碼。
- 添加 Cookie:
Cookie cookie1 = new Cookie("username", URLEncoder.encode(username,"UTF-8"));
- 讀取 Cookie:
Cookie[] cookies = request.getCookies();
if(cookies != null){for(Cookie cookie : cookies){if(cookie.getName().equals("username"))username = URLDecoder.decode(cookie.getValue(),"UTF-8");if(cookie.getName().equals("password"))password = cookie.getValue();}
}
Cookie 的瀏覽器管理
- Google Chrome:查看 Cookie 可通過相關入口操作;清理 Cookie 則在設置 - 隱私設置和安全性 - 清除瀏覽數據中進行。
- Firefox:查看 Cookie 有相應的操作入口;清理 Cookie 在選項 - 隱私與安全 - Cookie 和網站數據 - 清除數據中進行。
Cookie 的禁用
????????默認情況下瀏覽器啟用 Cookie,手動禁用后,絕大多數互聯網網站無法登錄,這與后續要講的 session 有關。判斷瀏覽器是否禁用 Cookie,可通過嘗試獲取剛剛添加的 Cookie,若獲取不到,則說明 Cookie 被禁用。
二、HttpSession:服務器端的會話對象
HttpSession 概述
????????Session 也是域對象之一,其范圍在一個會話范圍內有效,擁有getAttribute()
和setAttribute()
等系列方法。在一個會話內共享一個 session 對象,可保存會話內的數據,如當前用戶的信息。
目前所學域對象的作用范圍:ServletContext > HttpSession > HttpServletRequest。
????????獲取 session 對象可使用request.getSession()
方法。有了 session,就無需再用 Cookie 跟蹤會話,但 session 的生命周期相對較短,用戶關閉瀏覽器窗口后,session 就會失效。
HttpSession 原理(依賴 Cookie)
HTTP 是無狀態協議,但 session 能跟蹤會話狀態,這是因為 session 依賴 Cookie。
- 客戶端第一次訪問服務器時,服務器會為其創建一個 session 對象,放入 session 池中,并在響應時通過 Cookie 將 sessionId 發送給客戶端。只有第一次訪問時,服務器才會創建 session 并響應 sessionId。
- 客戶端再次訪問服務器時,會在請求中帶著 sessionId,服務器通過 sessionId 到 session 池中找到對應的 session 對象,從而完成會話跟蹤。服務器端保存 session 對象,客戶端只保存 sessionId,用戶在 session 中保存的數據就能被再次使用。
發送 sessionId 的 Cookie 的 maxAge 為 - 1,即只在瀏覽器內存中存在,關閉所有瀏覽器窗口后,該 Cookie 就會消失。
HttpSession 失效
session 失效主要有以下幾個原因:
- 調用
session.invalidate()
方法注銷 session。 - session 超時,可在配置中設置,如:
<session-config><!-- session的超時時間,以分鐘為單位 --><session-timeout>1</session-timeout>
</session-config>
- Cookie 被禁用,因為 session 依賴 Cookie 傳遞 sessionId,Cookie 禁用后,session 也會失效(不過可通過其他方式實現,如在 url 中傳遞 session_id)。
三、Session 和 Cookie 的區別(面試常考)
區別 | Session | Cookie |
---|---|---|
存儲位置 | 服務器端 | 客戶端(瀏覽器) |
存儲位置 | 默認存在服務器的一個文件里(不是內存) | 客戶端瀏覽器中 |
運行依賴 | 依賴 session id,而 session id 通常存在 cookie 中 | 不依賴其他存儲對象 |
存儲位置 | 可放在文件、數據庫、內存中 | 存于客戶端瀏覽器 |
應用場景 | 常用于用戶驗證等場合 | 用于跟蹤客戶端狀態,如保存用戶偏好設置等 |
四、綜合練習:使用 cookie 實現自動登陸
實現思路
通過在客戶端保存包含用戶名和密碼信息的 Cookie,當用戶再次訪問時,服務器讀取 Cookie 中的信息進行自動登錄驗證。
代碼實現
- html 代碼:
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Title</title>
</head>
<body>
<form action="login" method="post">用戶:<input type="text" name="username"/><br><br>密碼:<input type="password" name="password"/><br><br><input type="checkbox" value="1" name="auto"/> 一天內自動登陸<br><br><input type="submit" value="登陸"/><br>
</form>
</body>
</html>
- Java 代碼:
package cn.tx.servlet;import cn.tx.model.User;
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.*;
import java.io.IOException;/*** 模擬自動登陸方法*/
@WebServlet(name = "LoginServlet",urlPatterns = "/login")
public class LoginServlet extends HttpServlet {protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {// 獲取用戶端提交的表單信息String username = request.getParameter("username");String password = request.getParameter("password");String auto = request.getParameter("auto");// 統一設置response響應格式及編碼response.setContentType("text/html;charset=utf-8");response.setCharacterEncoding("UTF-8");// 判斷是否提交用戶信息if(username == null && password == null && auto == null){// 用來接收cookie的value信息String cookieValue = null;// 獲取用戶端cookiesCookie[] cookies = request.getCookies();// 如果cookies不為null 嘗試獲取name為txjavac的cookie的valueif(cookies != null){for(Cookie ck : cookies){if(ck.getName().equals("txjavac")){cookieValue = ck.getValue();}}}// 如果沒有獲取到cookie的value 返回信息if(cookieValue == null){response.getWriter().write("您還未進行登陸,請進行登陸!!!");return;}else{// 如果獲取到// 對該信息進行解碼BASE64Decoder decoder = new BASE64Decoder();cookieValue = new String(decoder.decodeBuffer(cookieValue));// 對解碼后的字符串進行切分String[] split = cookieValue.split(":");// 獲取用戶名和密碼username = split[1];password = split[2];// 創建用戶對象User user = new User(username,password);// 獲取sessionHttpSession session = request.getSession();// 把用戶對象存儲到session中session.setAttribute("user",user);// 返回內容response.getWriter().write("尊敬的"+username+",歡迎您!!!");return;}}// 判斷用戶名或密碼是否正確if(username!=null && password!=null && username.equals("admin") && password.equals("txjava")){// 創建用戶對象User user = new User(username,password);// 獲取session會話對象HttpSession session = request.getSession();// 把用戶存入session中session.setAttribute("user",user);// 用戶名密碼正確的話,判斷是否勾選了一天內自動登陸if(auto != null && auto.equals("1")){// 拼接存儲于cookie的value值String value = "txjava:" + username + ":" + password + ":" + 24*3600;// 進行BASE64編碼BASE64Encoder encoder = new BASE64Encoder();value = encoder.encode(value.getBytes());// 創建cookie并且設置一天失效后添加到responseCookie cookie = new Cookie("txjavac",value);cookie.setMaxAge(24*3600);response.addCookie(cookie);}response.getWriter().write("尊敬的"+username+",歡迎您!!!");return;}else {response.getWriter().write("用戶名或密碼錯誤!!!");return;}}protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {doPost(request,response);}
}
五、學習目標回顧
- 熟練掌握 Cookie 的概念、使用及生命周期。
- 能夠使用 Cookie 存儲中文并了解 Cookie 的路徑作用。
- 掌握 Session 的原理及使用方法。
- 能夠詳細說明 Cookie 和 Session 的區別。
- 能夠獨立完成自動登陸功能的開發。
????????通過以上內容,相信大家對 Cookie 和 Session 有了較為全面的認識,在實際開發中,可根據具體需求靈活運用這兩種技術來實現會話跟蹤等功能