你真的瞭解 Session 和 Cookie 嗎?
我是陳皮,一個在網際網路 Coding 的 ITer,微信搜尋「陳皮的JavaLib」第一時間閱讀最新文章,回覆【資料】,即可獲得我精心整理的技術資料,電子書籍,一線大廠面試資料和優秀簡歷模板。
前言
我們知道,HTTP 是無狀態的協議,服務端並不知道哪個請求是哪個使用者發起的。有些場景我們需要知道請求是哪個使用者發起的,哪個使用者操作的。例如商城服務,使用者發起請求下單,服務端需要識別是哪個具體的使用者。所以服務端需要使用某種機制來識別,記錄使用者的資訊,狀態等。
Session 機制就能實現,它可以讓無狀態協議的 HTTP 有狀態化。服務端為每個請求服務端的使用者建立其獨享的 Session,用於標識,跟蹤此使用者。Session 是儲存在服務端的,可以儲存在檔案,記憶體,資料等等,並且有唯一的標識 Session ID。服務端建立 Session 之後,服務端通過 HTTP 協議告訴客戶端,在本地 Cookie 中記錄這個 Session ID。這樣同個客戶端以後的每一個請求將 Cookie 一起傳送給服務端,服務端通過儲存在 Cookie 的 Session ID 查出儲存在服務端的 Session ,就能知道此次請求的是哪個使用者了。
Session
Session 中文意思即會話,時效。其實就是客戶端和服務端一對一互動的會話狀態,是一種抽象概念。很多人認為 Session 就是以下程式碼獲取的 Session 物件,其實這只是其中的藉助 Cookie 的一種通用性較好的實現而已。Session 有很多種實現的。
HttpSession session = request.getSession();
因為大部分應用程式藉助 Cookie 來實現 Session 跟蹤,即上述那一行程式碼。Cookie 是實際存在的。客戶端請求服務端並且第一次建立 Session 的時候,服務端通過 HTTP 協議(HTTP響應頭的 Set-Cookie
JSESSIONID
。
這樣同個客戶端以後的每一個請求會將 Cookie 一起傳送給服務端,服務端通過儲存在 Cookie 的 Session ID 查出儲存在服務端的 Session ,就能知道此次請求的是哪個使用者了。
HttpSession session = request.getSession();
不過客戶端瀏覽器是可以禁用 Cookie 的,那這種方式就會出現問題。但是我們可以使用 URL 重寫的技術來實現 Session 跟蹤,即在請求服務端的所有請求引數增加一個代表使用者標識或者 Session ID 即可。
http://chenpi.com/list?sid=xxx
前面我們說過 Session 可以儲存在檔案,記憶體,資料庫等地方。會話資訊具體儲存在哪裡其實都得根據自身業務來定,一切脫離業務場景談技術架構的都是耍流氓, 技術本身無好壞,不過是什麼業務場景適合什麼技術而已,這也是架構師考慮技術選型的一方面能力。
不過 Session 機制在叢集服務中需要考慮 Session 一致性問題。可以在叢集服務中做 Session 同步,不過這種方法有一些缺點,例如同步麻煩,同步延遲,多機儲存相同的 Session 浪費儲存空間。另外一種比較常用的方法就是使用專門的 Session 服務叢集來儲存使用者會話資訊,例如 Redis 快取服務,不僅可搭建叢集模式實現高可用可拓展,而且基於記憶體效能速度快。
public UserContext getUserContext(HttpServletRequest request) {
String userToken = getUserToken(request, COOKIE_KEY);
if (!StringUtils.isEmpty(userToken)) {
String userContextStr = redisUtils.getString(RedisKeyUtil.genKey(userToken));
if (!StringUtils.isEmpty(userContextStr)) {
return JSON.parseObject(userContextStr, UserContext.class);
}
}
return null;
}
public String getUserToken(HttpServletRequest request, String cookieName) {
Cookie[] cookies = request.getCookies();
if (null != cookies) {
for (Cookie cookie : cookies) {
if (Objects.equals(cookie.getName(), cookieName)) {
return cookie.getValue();
}
}
}
return null;
}
Cookie
Cookie 是客戶端技術,也是很多人實現 Session 會話的選型,服務端可以讓客戶端將一些資訊寫入本地 Cookie 中,來達到會話跟蹤的目的。不過要注意瀏覽器本地禁用 Cookie 的情況。
說到 Cookie,就不得不說很多廣告商,網站等採用我們個人隱私進行跟蹤,分析我們的行為,進行個性化推薦。很多網站利用第三方 Cookie
獲取使用者資訊,傳送到服務端記錄使用者的行為軌跡。你肯定也遇到在其他應用討論到防脫髮,然後你開啟淘寶驚奇的發現給你推薦各種防脫髮洗髮液。不過,目前有些瀏覽器已經禁用第三方 Cookie 或者進行優化處理了,例如 Safari,Mozilla 等。
我們可以手動將一些資訊設定到 Cookie 中,這樣客戶端不僅可以使用到這些資訊,在後續的請求中,服務端也能根據此資訊做相應的處理。
public void saveUserContext(HttpServletResponse response, String key, String value) {
// 設定cookie
Cookie cookie = new Cookie(key, value);
cookie.setPath("/");
// 設定有限期,負數例如-1代表Web瀏覽器關閉的時候刪除,如果不設定就預設-1
cookie.setMaxAge(12 * 60 * 60);
response.addCookie(cookie);
}
我們可以通過瀏覽器檢視儲存在本地的 Cookie 資訊,而且其他網站也可以掃描使用我們儲存的 Cookie,所以一些安全性或者保密的資訊儘量不要儲存在 Cookie 中,因為資料安全性比較低。正常情況下,使用者登入資訊等比較重要資訊儲存在服務端 Session 中,其他資訊例如會話 ID 可以儲存在 Cookie 中。
而且單個 Cookie 的大小也是有限制的,不同瀏覽器限制規則不一樣,一般大小是幾 Kb。 不同瀏覽器對於一個域名下的 Cookie 數量也是有限制的,一般就幾十個,而且也有數量飽和時淘汰策略,所以使用要注意這些情況,儘量不要超過瀏覽器的限制。