目錄
一、 HTTP協(xié)議
1.1 為什么HTTP協(xié)議是無狀態(tài)的?
1.2 在HTTP協(xié)議中流式傳輸和分塊傳輸編碼的區(qū)別
二、Cookie和Session
2.1 Cookie
2.2 Session
2.3 Cookie和Session的區(qū)別
2.4?瀏覽器端禁用了cookie,session能否正常使用呢?
三、servlet中與Cookie和Session相關(guān)的API
3.1 HttpServletRequest 類中的相關(guān)方法:
3.2 HttpServletResponse 類中的相關(guān)方法
3.3 HttpSession 類中的相關(guān)方法?
3.4 Cookie 類中的相關(guān)方法
四、實現(xiàn)模擬登錄流程
一、 HTTP協(xié)議
我們知道,HTTP協(xié)議是一種無狀態(tài)協(xié)議,這意味著每個HTTP請求都是獨立的,服務器不會記住之前的任何請求,也不會記錄當前請求與之前請求的關(guān)系。每個請求都是獨立的、不相關(guān)的,服務器不會保留任何有關(guān)請求或響應的信息,也不會保存客戶端的狀態(tài)。
1.1 為什么HTTP協(xié)議是無狀態(tài)的?
-
簡化服務器設計: 無狀態(tài)性使得服務器不需要為每個客戶端維護狀態(tài)信息。這降低了服務器的復雜性,減少了服務器端的資源消耗。服務器可以獨立處理每個請求,而不需要關(guān)心之前或之后的請求。
-
提高可伸縮性: 無狀態(tài)性有助于提高服務器的可伸縮性。由于服務器不需要維護客戶端的狀態(tài)信息,它可以輕松地處理大量并發(fā)請求,而無需擔心與之前請求的狀態(tài)沖突或耦合。
-
容錯性: 無狀態(tài)性使得系統(tǒng)更加容錯,因為即使某個請求或會話失敗,整個系統(tǒng)仍然可以繼續(xù)工作??蛻舳丝梢灾匦掳l(fā)起一個新的請求,而不必擔心服務器的狀態(tài)。
-
緩存支持: 無狀態(tài)性有助于緩存服務器的實現(xiàn)。由于每個請求都是獨立的,可以輕松地緩存和重用響應,從而提高性能和減少網(wǎng)絡流量。
-
簡化客戶端: 無狀態(tài)性也簡化了客戶端的設計??蛻舳瞬恍枰谡埱笾g維護服務器的狀態(tài)信息,因此客戶端可以更加簡單和通用化。
雖然HTTP本身是無狀態(tài)的,但為了支持某些應用程序的需要,通常會使用會話管理機制(如Cookie或會話標識符)來在一系列請求之間維護客戶端狀態(tài)。這種會話管理機制允許在HTTP請求之間傳遞和維護狀態(tài)信息,以便實現(xiàn)用戶身份驗證、購物車管理等功能。但這些機制通常是基于無狀態(tài)的HTTP協(xié)議之上構(gòu)建的,以平衡簡化性和功能需求。
需要注意的是。雖然HTTP協(xié)議為無狀態(tài)協(xié)議,但是與HTTP支持長連接不沖突。
長連接(也稱為持久連接)是一種HTTP/1.1協(xié)議引入的技術(shù),它允許客戶端和服務器在一個TCP連接上發(fā)送多個HTTP請求和響應,而不需要為每個請求/響應都建立一個新的TCP連接。這種技術(shù)可以顯著減少網(wǎng)絡連接的建立和關(guān)閉開銷,提高網(wǎng)絡傳輸效率,同時也能夠更好地支持HTTP協(xié)議的特性,如流式傳輸和分塊傳輸編碼等。
在長連接中,客戶端和服務器之間的TCP連接在一個HTTP請求/響應完成后不會立即關(guān)閉,而是繼續(xù)保持連接狀態(tài),等待下一個HTTP請求/響應。這樣,當客戶端發(fā)送下一個HTTP請求時,可以直接利用之前的TCP連接,不需要再進行TCP握手和揮手等操作,從而節(jié)省了網(wǎng)絡資源和時間。
因此,雖然HTTP協(xié)議是無狀態(tài)的,但是通過使用長連接技術(shù),可以在保持無狀態(tài)的前提下提高HTTP協(xié)議的效率和性能。需要注意的是,長連接需要服務器和客戶端都支持,否則無法建立長連接。同時,長連接也可能會導致資源占用問題,因此需要合理使用和配置。
以下為瀏覽器請求BIng主頁的抓包信息(Connection:keep-alive就為長連接的意思):
在HTTP/1.1中,如果客戶端請求頭中沒有明確指定Connection頭字段,那么服務器會默認使用長連接,即保持TCP連接處于打開狀態(tài),直到客戶端或服務器明確要求關(guān)閉連接為止。這種方式可以減少每次請求和響應時建立和關(guān)閉TCP連接的開銷,提高網(wǎng)絡傳輸效率和性能。但需要注意的是,長連接并不是在所有情況下都適用,有時候也需要根據(jù)實際情況關(guān)閉連接。
1.2 在HTTP協(xié)議中流式傳輸和分塊傳輸編碼的區(qū)別
流式傳輸和分塊傳輸編碼都是HTTP協(xié)議中用于數(shù)據(jù)傳輸?shù)募夹g(shù),它們的區(qū)別如下:
-
數(shù)據(jù)分割方式不同:流式傳輸是將響應數(shù)據(jù)(如HTML,JSON等)按照一定的塊大小進行分割,逐步發(fā)送到客戶端;而分塊傳輸編碼是將數(shù)據(jù)分成若干個塊(Chunk),每個塊有獨立的長度信息和數(shù)據(jù)內(nèi)容,使用分塊傳輸編碼方式將數(shù)據(jù)分成多個塊進行傳輸,每個塊之間用一個CRLF(回車換行)分隔符分開。
-
響應方式不同:流式傳輸是一次性發(fā)送響應數(shù)據(jù),客戶端在接收到第一個數(shù)據(jù)塊時就可以開始處理;而分塊傳輸編碼則是將響應數(shù)據(jù)分成多個塊逐個發(fā)送,客戶端需要在接收完所有塊后再進行處理。
-
大小控制不同:流式傳輸中每個數(shù)據(jù)塊的大小是固定的,由服務器決定;而分塊傳輸編碼中每個塊的大小是不固定的,客戶端需要通過讀取長度信息來確定每個塊的大小。
-
讀取速度,應用場景不同:流式傳輸是在客戶端接收到第一個數(shù)據(jù)塊時就可以開始處理,而不必等待整個響應數(shù)據(jù)全部接收完畢。這種方式可以減少客戶端等待時間,提高響應速度。流式傳輸常用于需要實時更新的數(shù)據(jù)場景,如股票行情、新聞快訊等。而分塊傳輸編碼客戶端接收到每個塊后,會先讀取塊的長度信息,再讀取對應長度的數(shù)據(jù)內(nèi)容。這種方式可以避免一次性傳輸大量數(shù)據(jù)造成的網(wǎng)絡擁塞和連接中斷,并且可以讓客戶端在接收到部分數(shù)據(jù)時就開始處理,從而提高傳輸效率和響應速度。
總之,流式傳輸和分塊傳輸編碼都可以提高HTTP數(shù)據(jù)傳輸?shù)男屎晚憫俣?,但應根?jù)具體的場景和需求進行選擇和使用。流式傳輸適用于需要實時更新的數(shù)據(jù)場景,如股票行情、新聞快訊等;而分塊傳輸編碼適用于需要傳輸大文件的場景,能夠避免一次性傳輸造成的網(wǎng)絡擁塞和連接中斷。
二、Cookie和Session
2.1 Cookie
上面我們說到,由于HTTP是無狀態(tài)的,所以需要引入Cookie來解決這一問題。
Cookie允許服務器在客戶端上存儲少量的數(shù)據(jù),例如:用戶的身份,偏好,購物車信息以及與客服對話等。以便在之后的HTTP請求中將該數(shù)據(jù)發(fā)送回服務器,通過Cookie,服務器可以識別和跟蹤用戶的身份和狀態(tài),從而提供更加個性化和定制化的服務。
舉個例子:就比如在我們使用淘寶的時候,與客服進行對話,有的時候我們并不是一直在對話界面等待對方回復,而是需要切到其他APP中,過了一會再回來的時候詢問客服的時候,客服就會通過客戶請求時候所發(fā)送的Cookie來查看上下文,進而實現(xiàn)后續(xù)的對話。
回顧以下,Cookie是什么,從哪里來,發(fā)送到哪去,存儲在哪?
- 是什么:Cookie是一種由服務器發(fā)送到客戶端的小型數(shù)據(jù)文件,它包含了有關(guān)用戶和網(wǎng)站之間的交互信息,以及有關(guān)用戶的偏好和狀態(tài)信息,
- 從哪里來:當服務器想要在客戶端上創(chuàng)建或者添加一個新的Cookie時候,會將Set-Cookie字段添加到HTTP響應頭中,告訴客戶端應該如何創(chuàng)建或跟新Cookie(Set-Cookie中是由開發(fā)者自己定義的鍵值對)。
- 發(fā)送到哪去:客戶端在接收到帶有Set-Cookie字段的HTTP響應時,會解析該字段,并根據(jù)其中的信息在本地創(chuàng)建或更新對應的Cookie。之后,在該客戶端向同一服務器發(fā)出HTTP請求時,該Cookie信息會被自動添加到HTTP請求頭中,從而讓服務器能夠識別并跟蹤用戶的身份和狀態(tài)。
- 存儲在哪:在客戶端,Cookie通常存儲在Web瀏覽器所在的硬盤中,瀏覽器會根據(jù)域名來分別存儲。
對于第四點,可以在瀏覽器的URL處,點擊查看:
Cookies中的內(nèi)容通常是純文本,而且通常是以ASCII字符集編碼的,因此在大多數(shù)情況下,Cookies中的值是由英文字符和數(shù)字組成的:?
2.2 Session
Session(會話)是Web應用程序中的一個概念,它指的是一系列相關(guān)的HTTP請求和響應,通常用于在客戶端和服務器之間維護一些狀態(tài)信息。在一個會話中,服務器會創(chuàng)建一個唯一的Session ID(會話標識符),并將該Session ID發(fā)送給客戶端瀏覽器。客戶端在之后的每個HTTP請求中都會攜帶該Session ID,以便服務器能夠識別并跟蹤該客戶端的身份和狀態(tài)。
為了加深理解,我們來看一組登錄流程:
Session通常是基于Cookie實現(xiàn)的。具體來說,當服務器創(chuàng)建一個新的Session時,它會在響應頭中添加一個Set-Cookie字段,其中包含了一個名為SESSIONID的Cookie值,該值就是新創(chuàng)建的Session ID??蛻舳嗽谑盏皆擁憫獣r會將該Cookie值保存在瀏覽器中,并在之后的每個HTTP請求中發(fā)送回服務器。服務器在接收到每個HTTP請求時都會檢查該請求中是否包含了有效的Session ID,如果存在則說明該請求屬于某個已經(jīng)創(chuàng)建的Session,服務器就可以根據(jù)該Session ID來識別和跟蹤客戶端的身份和狀態(tài)。
需要注意的是:Cookie是有存儲期限的,越敏感的網(wǎng)站存儲期限越短。
2.3 Cookie和Session的區(qū)別
Cookie和Session是Web開發(fā)中常用的兩種技術(shù),用于在服務器和客戶端之間保持狀態(tài)信息。它們的區(qū)別如下:
-
存儲位置不同:Cookie是存儲在客戶端的瀏覽器中的文本文件,而Session是存儲在服務器端的內(nèi)存中或者數(shù)據(jù)庫中的鍵值對。
-
安全性不同:由于Cookie是存儲在客戶端中的,所以存在被竊取或者篡改的風險。而Session存儲在服務器端,相對來說更加安全。
-
存儲容量不同:Cookie的存儲容量較小,通常為4KB左右,而Session的存儲容量可以比較大,但會占用服務器的內(nèi)存或者存儲資源。
-
生命周期不同:Cookie可以設置過期時間,可以長期保存在客戶端的瀏覽器中;而Session在用戶關(guān)閉瀏覽器或者超過一定時間后,會自動銷毀。
-
使用方式不同:Cookie是通過在服務器端發(fā)送HTTP響應頭來設置的,客戶端的瀏覽器會自動保存Cookie,下一次請求時會自動發(fā)送Cookie到服務器端。而Session需要在服務器端通過某種方式生成Session ID,并將Session ID存儲在Cookie中或者URL參數(shù)中,客戶端瀏覽器會將Session ID發(fā)送到服務器端,服務器根據(jù)Session ID來獲取Session數(shù)據(jù)。
綜上所述,Cookie和Session都可以用于在服務器和客戶端之間保持狀態(tài)信息,但它們的應用場景和特點有所不同,具體使用哪種技術(shù)要根據(jù)實際需求和安全要求來決定。
2.4?瀏覽器端禁用了cookie,session能否正常使用呢?
???????當瀏覽器禁用了Cookie時,會影響到傳統(tǒng)的基于Cookie的Session管理,因為Session通常依賴于瀏覽器發(fā)送的Cookie來標識和維護會話狀態(tài)。如果瀏覽器禁用了Cookie,會導致Session無法正常工作,因為無法在客戶端存儲Session標識信息。
????????但是,有一種替代方法可以在瀏覽器禁用Cookie時維護Session狀態(tài),那就是URL重寫或稱為URL重定向。這種方法將會話標識信息添加到URL中,而不是依賴于Cookie。
以下是如何使用URL重寫來維護Session狀態(tài)的一般步驟:
-
當用戶訪問網(wǎng)站時,服務器會為其創(chuàng)建一個唯一的會話標識,通常是一個長字符串,將其存儲在服務器端。
-
對于每個后續(xù)的請求,服務器會將該會話標識信息附加到URL中,例如:http://example.com/page?sessionid=abcdef123456.
-
服務器接收到請求后,會檢查URL中的會話標識信息,并將其與服務器端存儲的會話狀態(tài)相關(guān)聯(lián)。
-
服務器根據(jù)會話標識提供相應的會話數(shù)據(jù),如用戶登錄狀態(tài)、購物車內(nèi)容等。
-
當用戶發(fā)出下一個請求時,會話標識信息將繼續(xù)被傳遞,以維護會話狀態(tài)。
使用URL重寫來維護會話狀態(tài)的方法,即使瀏覽器禁用了Cookie,也能正常工作。然而,需要注意的是,這會導致URL包含會話標識信息,可能會有一些安全和隱私問題。因此,在實現(xiàn)時,需要謹慎處理敏感信息,并采取適當?shù)陌踩胧?,如使用HTTPS來加密通信。
三、servlet中與Cookie和Session相關(guān)的API
3.1 HttpServletRequest 類中的相關(guān)方法:
方法 | 描述 |
HttpSession? getSession() | 在服務器中獲取會話. 參數(shù)如果為 true, 則當不存在會話時新建會話; 參數(shù)如果 為 false, 則當不存在會話時返回 null |
Cookie[] getCookies() | 返回一個數(shù)組, 包含客戶端發(fā)送該請求的所有的 Cookie 對象. 會自動把 Cookie 中的格式解析成鍵值對. |
3.2 HttpServletResponse 類中的相關(guān)方法
方法 | 描述 |
void addCookie(Cookie cookie) | 把指定的 cookie 添加到響應中. |
3.3 HttpSession 類中的相關(guān)方法?
方法 | 描述 |
Object getAttribute(String name) | 該方法返回在該 session 會話中具有指定名稱的對象,如果沒 有指定名稱的對象,則返回 null. |
void setAttribute(String name, Object value) | 該方法使用指定的名稱綁定一個對象到該 session 會話 |
boolean isNew() | 判定當前是否是新創(chuàng)建出的會話 |
3.4 Cookie 類中的相關(guān)方法
每個 Cookie 對象就是一個鍵值對
方法 | 描述 |
String getName() | 該方法返回 cookie 的名稱。名稱在創(chuàng)建后不能改變。(這個值是 Set Cooke 字段設置給瀏覽器的) |
String getValue() | 該方法獲取與 cookie 關(guān)聯(lián)的值 |
void setValue(String newValue) | 該方法設置與 cookie 關(guān)聯(lián)的值。 |
- HTTP 的 Cooke 字段中存儲的實際上是多組鍵值對. 每個鍵值對在 Servlet 中都對應了一個 Cookie對象.
- 通過 HttpServletRequest.getCookies() 獲取到請求中的一系列 Cookie 鍵值對.
- 通過 HttpServletResponse.addCookie() 可以向響應中添加新的 Cookie 鍵值對.
四、實現(xiàn)模擬登錄流程
第一步,約定前后端接口。
我們需要實現(xiàn)兩套交互邏輯,一是登錄跳轉(zhuǎn),二是獲取主頁。
登錄跳轉(zhuǎn)約定:
約定使用POST請求,響應采用302重定向。
package login;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
/**
* Created with IntelliJ IDEA.
* Description:
* User: 86136
* Date: 2023-04-01
* Time: 18:53
*/
@WebServlet("/loginServlet")
public class LoginServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String username = req.getParameter("username");
String password = req.getParameter("password");
//因為這里我們主要是演示Cookie和Session,所以為了簡化流程,并不將用戶名和密碼
//存入到數(shù)據(jù)庫中,而是直接定死。約定用戶名合法為 Jay 和 Lin
//密碼合法為123。
if(!username.equals("Jay") && !username.equals("Lin")) {
//登錄失敗
System.out.println("用戶名錯誤");
resp.sendRedirect("login.html");
return;
}
if (!password.equals("123")) {
//登錄失敗
System.out.println("密碼錯誤");
resp.sendRedirect("login.html");
return;
}
//登錄成功
//1.創(chuàng)建一個會話
HttpSession session = req.getSession(true);
//2.把當前的用戶名保存在會話中
//void setAttribute(String var1, Object var2);
session.setAttribute("username",username);
//初始情況下設置登錄次數(shù)
session.setAttribute("count",0);
//3.重定向到主頁
resp.sendRedirect("indexServlet");
}
}
分析:
其中的getSession(true):判定當前請求是否已經(jīng)有對應的會話(拿著來自客戶端的請求中Cookie里的sessionId查一下),如果sessionId不存在,或者沒有被查到,那么就創(chuàng)建一個新的會話,并插入到哈希表中。如果查到了,就直接返回查到的結(jié)果。?
創(chuàng)建新的會話的流程如下:構(gòu)造一個HttpSession對象,構(gòu)造唯一的sessionId,把這個鍵值對插入到哈希表中,最后把sessionId設置到響應報文的Set-Cookie字段中,有服務器發(fā)送給瀏覽器。
獲取主頁約定:
采用GET請求,響應返回一個頁面:
package login;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
@WebServlet("/indexServlet")
public class IndexServlet extends HttpServlet {
//因為通過瀏覽器是通過重定向來發(fā)送請求的,所以以下為doGet
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//需要先判定用戶的登錄狀態(tài)
//如果用戶沒有登錄,要求先登錄,如果登錄了,則根據(jù) 會話 中的用戶名,來顯示到頁面上
//這個操作不會觸發(fā)會話的創(chuàng)建
HttpSession session = req.getSession(false);
if (session == null) {
//未登錄狀態(tài)
System.out.println("用戶未登錄");
}
//已經(jīng)登錄,取出會話信息
String username = (String) session.getAttribute("username");
Integer cnt = (Integer) session.getAttribute("count");
//訪問次數(shù)加1
cnt++;
//寫回到會話中
session.setAttribute("count",cnt);
//構(gòu)造頁面
resp.setContentType("text/html; charset=utf8");
resp.getWriter().write("<h3>歡迎" + username + "</h3> <h4>這個頁面已經(jīng)被訪問了"+cnt+"次</h4>");
}
}
第二步,編寫前端交互頁面
我們的重點是來學習登錄的邏輯,因此登錄的界面不需要很好看很復雜,只要能夠有兩個輸入框和一個提交按鈕讓我們輸入賬號密碼就行。目標頁面如下:
前面我們約定了登錄的跳轉(zhuǎn)采用post請求,由于場景很簡單,我們直接使用form表單構(gòu)造post請求就可以了:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<form action="loginServlet" method="post">
<input type="text" name="username">
<br>
<input type="password" name="password">
<br>
<input type="submit" name="提交">
</form>
</body>
</html>
其中input標簽的name屬性就對應鍵值對的key,輸入的內(nèi)容就對應鍵值對的value。
效果演示:
?
下面利用抓包結(jié)果來進一步加深理解:
第一次交互
請求部分:注意,第一次請求是沒有Cookie的。
?服務器返回給登錄界面的響應部分:
?第二次交互
在登錄狀態(tài)下,主頁向服務器發(fā)起訪問請求:
這個帶有Cookie的get請求到達服務器的時候,Servlet會在getSession方法中根據(jù)sessionId來查詢HttpSession對象:
?由于sessionId查的到,于是就返回信息。
?服務器返回給瀏覽器的響應部分:文章來源:http://www.zghlxwxcb.cn/news/detail-413726.html
文章來源地址http://www.zghlxwxcb.cn/news/detail-413726.html
到了這里,關(guān)于Cookie和Session的工作流程及區(qū)別(附代碼案例)的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!