全方位解析!會話、Cookie、令牌與JWT的工作原理與實際應用
在現代互聯網應用中,安全性和用戶體驗是兩個至關重要的因素。隨著移動設備的普及和分布式系統的興起,傳統的 Session 認證方式逐漸顯露出其局限性。Token 認證作為一種新興的身份驗證機制,因其無狀態的特性和良好的擴展性而受到廣泛關注。Token 的使用不僅簡化了用戶的登錄流程,還有效減輕了服務器的負擔。在本文中,我們將深入探討 Token 的定義、組成和優勢,特別是在電商平臺和第三方服務集成中的應用。我們還將介紹 JSON Web Token(JWT),作為 Token 的一種特定形式,JWT 在實現無縫用戶體驗和增強安全性方面具有顯著優勢。
HTTP協議是一個“無狀態協議”,即每當服務器收到客戶端的請求時,這都是一個全新的請求,服務器并不知道客戶端的歷史請求記錄。Session和Cookie的主要目的就是彌補HTTP的無狀態特性。
什么是Session?
當客戶端請求服務器時,服務器會為該請求打開一個“內存空間”。這個內存空間存儲Session對象,存儲結構是ConcurrentHashMap。Session彌補了HTTP的無狀態特性。服務器可以通過Session存儲客戶端在同一會話期間的一些操作記錄。
如何判斷是否是同一會話?
當服務器第一次接收到請求時,會打開一個Session空間(創建一個Session對象),同時生成一個sessionId,并通過響應頭中的Set-Cookie: JSESSIONID=XXX命令向客戶端發送響應,請求設置Cookie。
客戶端收到響應后,會在本地設置一個JSESSIONID=XXX的Cookie信息。這個Cookie的過期時間是瀏覽器會話結束時。
下次當客戶端向同一網站發送請求時,請求頭將攜帶這個Cookie信息(包括sessionId)。然后,通過讀取請求頭中的Cookie信息,服務器獲得名為JSESSIONID的值,并獲取該請求的sessionId。
Session的缺點
然而,Session機制有一個缺點。如果你的服務器進行了負載均衡,并在第一次請求時將Session存儲在服務器A上。假設在一段時間內,服務器A的流量激增,請求將被轉發到服務器B進行訪問。但是服務器B并不存儲服務器A的Session,這將導致Session失效。
圖片
什么是Cookie?
在介紹Session時,你應該注意到Cookie已經被提到。Session是基于Cookie實現的。Session存儲在服務器端,而sessionId則存儲在客戶端的Cookie中。
HTTP協議中的Cookies包括Web Cookie和瀏覽器 Cookie。它是服務器發送到Web瀏覽器的小塊數據。服務器發送給瀏覽器的Cookie將被瀏覽器存儲,并在下次請求時與其他請求一起發送回服務器。通常用于確定兩個請求是否來自同一瀏覽器,例如用戶保持登錄狀態時。
Cookies主要用于以下三個目的:
1. 會話管理
- 與服務器協作,通過存儲sessionid來識別用戶會話。
2. 存儲用戶信息
- 登錄狀態:記住用戶是否已登錄。下次訪問時無需再次登錄。
- 偏好設置:如語言、主題等。下次訪問時自動應用。
3. 跟蹤用戶行為
- 瀏覽歷史:記錄訪問的頁面,以便于推薦和導航。
- 分析行為:了解用戶習慣,以優化和精準營銷。
創建Cookies
當服務器接收到來自客戶端的HTTP請求時,可以發送帶有Set-Cookie頭的響應。Cookies通常由瀏覽器存儲,然后隨HTTP頭一起發送到服務器。
Set-Cookie和Cookie頭
Set-Cookie HTTP響應頭將Cookies從服務器發送到用戶代理。下面是發送Cookie的示例。
圖片
這個頭告訴客戶端存儲Cookies。
現在,每次向服務器發起新請求時,瀏覽器都會通過Cookie頭將所有先前存儲的Cookies發送回服務器。
圖片
有兩種類型的Cookies。一種是Session Cookies,另一種是Persistent Cookies。如果一個cookie不包含過期日期,它被視為會話cookie。Session cookies存儲在內存中,從不寫入磁盤。當瀏覽器關閉時,cookie將永久丟失。如果一個cookie包含“過期時間”,則被視為持久性cookie。在指定的過期日期,cookie將從磁盤中刪除。
還有“Secure和HttpOnly標志”。讓我們逐一介紹它們。
Session Cookies
上面的示例創建了一個會話cookie。會話cookie的一個特征是,當客戶端關閉時,該cookie將被刪除,因為沒有指定Expires或Max-Age指令。
但是,網頁瀏覽器可能會使用會話恢復,這將使大多數會話cookie保持在永久狀態,就好像瀏覽器從未關閉過。
持久性Cookies
持久性cookies在客戶端關閉時不會過期。相反,它們在“特定日期(Expires)”或“特定時間段(Max-Age)”后過期。例如:
Set-Cookie: id=a3fWa; Expires=Sat, 21 Sep 2024 11:28:00 GMT;
Secure和HttpOnly標志
Secure cookies需要通過HTTPS協議以加密方式發送到服務器。即使它們是安全的,敏感信息也不應存儲在cookies中,因為它們本質上是不安全的,這個標志并不能提供真正的保護。
HttpOnly的功能:
- 會話cookie中缺少HttpOnly屬性可能導致攻擊者通過程序(JS腳本、Applet等)獲取用戶的cookie信息,從而導致用戶cookie信息泄露,增加跨站腳本攻擊的威脅。
- HttpOnly是微軟對cookies的擴展。這個值指定cookies是否可以通過客戶端腳本訪問。
- 如果cookies中沒有將HttpOnly屬性設置為true,可能導致cookie被竊取。被竊取的cookies可能包含識別網站用戶的敏感信息,例如ASP.NET會話ID或Forms身份驗證票據。攻擊者可以重放被竊取的cookies偽裝成用戶,獲取敏感信息并進行跨站腳本攻擊。
Cookies的范圍
Domain和Path標識符定義了cookies的范圍:即cookies應發送到哪些URL。
Domain標識符指定可以接受cookies的主機。如果未指定,當前主機(不包括子域)為默認值。如果指定了Domain,通常會包括子域。
例如,如果設置Domain=mozilla.org,則cookies也包括子域(如developer.mozilla.org)。
例如,如果設置Path=/test,則以下地址都將匹配:
- /test
- /test/user/
- /test/user/login
為什么在已有Session的情況下還需要Token?
在現代Web開發中,雖然Session在一定程度上可以實現用戶認證和狀態管理,但它也有一些局限性。
Session的局限性
假設你在經營一個大型在線購物商城。當用戶登錄你的商城時,服務器會創建一個Session來記錄用戶的登錄狀態。這個Session就像在商城服務臺為用戶準備的專屬卡,記錄用戶的身份信息。
然而,當你的商城業務越來越繁忙,許多用戶同時在線購物時,服務器需要為每個用戶保存這個Session信息,這將占用大量的服務器內存資源。此外,如果你的商城使用多個服務器共享流量(例如通過負載均衡器),那么需要復雜的機制來確保當用戶在不同服務器之間切換時,他們的Session信息能夠正確傳輸和識別。否則,用戶可能會突然被登出或無法正常購物。
另外,假設用戶在手機上購物時,突然有緊急事務需要外出。這時,如果用戶再次使用另一設備(如平板)訪問你的商城,由于Session通常綁定在特定設備上,用戶可能需要重新登錄,這將給用戶帶來不便。
Token 的優勢
現在,讓我們介紹 Token。Token 就像一個神奇的通行證。當用戶成功登錄后,服務器會生成一個包含用戶身份信息的 Token,并將其返回給用戶。用戶可以將這個 Token 保存在自己的設備上(例如瀏覽器的本地存儲中)。
當用戶在商場瀏覽產品、加入購物車或結賬時,只需在每次請求中攜帶這個 Token。服務器收到請求后,可以通過驗證 Token 的有效性來判斷用戶的身份和權限,而不必查找和管理復雜的 Session 信息。
例如,用戶在手機上登錄商場并獲得 Token。當用戶外出并再次使用平板電腦訪問商場時,只需在平板瀏覽器中提供這個 Token,服務器就能立即識別用戶的身份,而用戶無需再次登錄。此外,無論商場中有多少用戶同時在線,服務器都無需為每個用戶保存大量的 Session 信息,只需驗證每次請求的 Token,大大減輕了服務器的負擔。
另外,Token 可以與第三方服務輕松集成。例如,如果商場想與外部支付服務合作,只需將 Token 傳遞給支付服務。支付服務可以通過驗證 Token 確定用戶的身份,而無需建立自己的 Session 管理機制。
現在讓我們詳細介紹 Token。
什么是 Token?
訪問 Token
訪問 Token 是訪問資源接口(API)時所需的憑證。
Token 的組成并不是固定的。一個簡單的 Token 組成包括:
- uid(用戶的唯一身份標識符);
- time(當前時間的時間戳);
- sign(簽名,是從 Token 的前幾個數字中通過哈希算法壓縮而成的十六進制字符串)。
import java.security.MessageDigest;
import java.util.Base64;
import java.util.Date;
public class TokenGenerator {
public static String generateToken(int uid) {
long time = new Date().getTime();
String tokenContent = uid + "-" + time;
// 為 Token 內容生成簽名。
String sign = generateSign(tokenContent);
return uid + "-" + time + "-" + sign;
}
// 使用 SHA-256 哈希算法為給定內容生成簽名。
private static String generateSign(String content) {
try {
MessageDigest digest = MessageDigest.getInstance("SHA-256");
byte[] hash = digest.digest(content.getBytes());
StringBuilder hexString = new StringBuilder();
for (byte b : hash) {
// 將每個字節轉換為兩位十六進制字符串。
String hex = Integer.toHexString(0xff & b);
if (hex.length() == 1) hexString.append('0');
hexString.append(hex);
}
return hexString.substring(0, 8);
} catch (Exception e) {
return null;
}
}
public static void main(String[] args) {
int uid = 1234;
String token = generateToken(uid);
System.out.println("生成的 Token: " + token);
}
}
輸出:
生成的 Token: 1234-1729530432169-3638dd14
它具有以下特點:
- 服務器是無狀態的,具有良好的可擴展性。
- 支持移動設備。
- 安全性足夠高。
- 支持跨程序調用。
Token 驗證過程:
圖片
從上述過程可以知道,Token 需要在每個后續請求中攜帶,因此 Token 需要放置在 HTTP Header 中。基于 Token 的用戶驗證是一種服務器端無狀態的驗證方法,服務器無需存儲 Token 數據。解析 Token 的計算時間換取了 Session 的存儲空間,從而減輕了服務器的壓力,減少了頻繁的數據庫查詢操作。
此外,Token 完全由應用程序自己管理,因此可以避免同源策略的限制。
Refresh Token 是另一種專用于刷新訪問 Token 的 Token。如果沒有 Refresh Token,訪問 Token 也可以被刷新,但每次刷新時用戶需要輸入登錄用戶名和密碼,這會非常麻煩。有了 Refresh Token,這個麻煩就可以減少。客戶端直接使用 Refresh Token 更新訪問 Token,而用戶無需進行額外操作。
訪問 Token 的有效期通常較短。當訪問 Token 由于過期變得無效時,可以使用 Refresh Token 獲取新的 Token。如果 Refresh Token 也過期,用戶只能重新登錄。
此外,Refresh Token 和過期時間存儲在服務器的數據庫中,僅在申請新訪問 Token 時進行驗證。這不會影響業務接口的響應時間,也不需要像 Session 一樣一直保存在內存中以處理大量請求。
JSON Web Token
Token 是一個更廣泛的概念,而 JSON Web Token(JWT)是一種具有特定結構和特性的 Token。JWT 在某些場景中具有優勢,例如需要自包含的認證信息、跨平臺使用,以及對可擴展性有較高要求的場景。我們將在后續文章中提供對 JWT 的具體介紹。
結論
通過對 Token 的深入分析,我們可以看到,它在現代 web 應用中的重要性不可忽視。Token 不僅為用戶提供了便捷的訪問方式,減少了重復登錄的麻煩,還通過無狀態的驗證機制大幅減輕了服務器的壓力。這種設計使得系統能夠更好地應對高并發的請求,確保良好的用戶體驗。
此外,Token 的靈活性使其能夠與第三方服務輕松集成,為開發者提供了更大的自由度。尤其是在需要與外部支付服務或其他 API 進行交互的場景中,Token 的使用簡化了身份驗證的復雜性,提升了整體效率。
最后,隨著對數據安全和隱私保護要求的提高,Token 認證特別是 JWT 的自包含特性,將在未來的開發中扮演更為重要的角色。我們期待在后續的文章中,進一步探討 JWT 的具體實現及其在各種場景下的應用潛力。通過掌握 Token 的工作原理和應用策略,開發者將能夠設計出更加安全、高效的系統,滿足不斷變化的市場需求。