Cookie
Cookie是由服務端生成的,發送給客戶端(通常是瀏覽器)的,保存在客戶端中,並且是可見的,客戶端的一些程序可能會篡改、窺探cookie中的內容。假如用到Cookie,比較好的方法是,敏感的信息如賬號密碼等儘量不要寫到Cookie中。最好是像Google、Baidu那樣將Cookie信息加密,提交到服務器後再進行解密,保證Cookie中的信息只要本人能讀得懂。(選擇Session就省事多了,反正是放在服務器上,Session裏任何隱私都能夠有效的保護)
按在客戶端中的存儲位置,可分爲內存Cookie和硬盤Cookie:
- 內存Cookie由瀏覽器維護,保存在內存中,瀏覽器關閉後就消失了,其存在時間是短暫的。
- 硬盤Cookie保存在硬盤裏,有一個過期時間,除非用戶手工清理或到了過期時間,硬盤Cookie不會被刪除,其存在時間是長期的。所以,按存在時間,可分爲非持久Cookie和持久Cookie。
登錄過Google,Google的登錄信息長期有效。用戶不用每次訪問都重新登錄,Google會持久地記載該用戶的登錄信息。要到達這種效果,運用Cookie會是比較好的選擇。只需要設置Cookie的過期時間屬性爲一個很大很大的數字。當用戶第一次訪問並登陸一個網站的時候,cookie的設置以及發送會經歷以下4個步驟:
- 客戶端發送一個請求到服務器
- 服務器返回一個HttpResponse響應到客戶端,其中設置了Set-Cookie的頭部(可以在服務端設置cookie.setHttpOnly(true)設置響應頭攜帶cookie信息)
- 客戶端保存cookie,之後向服務器發送請求時,HttpRequest請求中會包含一個Cookie的頭部
- 服務器返回響應數據
package com.cloud.ceres.rnp.control.web;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.util.Enumeration;
@RestController
@RequestMapping("/aop")
public class AopControl {
private static Logger logger = LoggerFactory.getLogger (AopControl.class);
@GetMapping("/queryXXX")
public String queryXXX() {
ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = requestAttributes.getRequest();
HttpServletResponse response = requestAttributes.getResponse();
Cookie[] cookies = request.getCookies();
if (cookies!= null) {
for (Cookie cookie : cookies) {
System.out.println(cookie.getName() + ":[cooke]:" + cookie.getValue());
cookie.setHttpOnly(true);
response.addCookie(cookie);
}
}
HttpSession session = request.getSession();
System.out.println("sessionId:" + session.getId());
Enumeration<String> attrs = session.getAttributeNames();
while(attrs.hasMoreElements()){
// 獲取session鍵值
String name = attrs.nextElement().toString();
// 根據鍵值取session中的值
Object vakue = session.getAttribute(name);
System.out.println(name + ":[session]" + vakue);
}
return "aopTest方法結束";
}
}
通過瀏覽器訪問該接口
第一次訪問:由於第一次是不存在cookie的,所以第一次訪問控制檯輸出如下:
sessionId:5FCF280DDF6F28C70C178B1FCBF12A01
第二次訪問:由於第一次訪問,所以第一次訪問控制檯輸出如下:
JSESSIONID:[cooke]:F9AF6D66D4BAC7B3033A5F29F838D033
sessionId:5FCF280DDF6F28C70C178B1FCBF12A01
實際上大多數的應用都是用 Cookie 來實現Session跟蹤的,第一次創建Session的時候(服務點自動會生成持久化保存在服務端),服務端會在HTTP協議中告訴客戶端,需要在 Cookie 裏面記錄一個Session ID,以後每次請求把這個會話ID發送到服務器,我就知道你是誰了。如果客戶端的瀏覽器禁用了 Cookie 怎麼辦?一般這種情況下,會使用一種叫做URL重寫的技術來進行會話跟蹤,即每次HTTP交互,URL後面都會被附加上一個諸如 sid=xxxxx 這樣的參數,服務端據此來識別用戶。
(一)Cookie的根本作用就是在客戶端存儲用戶訪問網站的一些信息。典型的應用有:
1、記住密碼,下次自動登錄。
2、購物車功能。
3、記錄用戶瀏覽數據,進行商品(廣告)推薦。
(二)缺陷
①Cookie會被附加在每個HTTP請求中,所以無形中增加了流量。
②由於在HTTP請求中的Cookie是明文傳遞的,所以安全性成問題。(除非用HTTPS)
③Cookie的大小限制在4KB左右。對於複雜的存儲需求來說是不夠用的。
參考:https://www.jianshu.com/p/6fc9cea6daa2
Session
在說session是啥之前,我們先來說說爲什麼會出現session會話,它出現的機理是什麼?我們知道,我們用瀏覽器打開一個網頁,用到的是HTTP協議,學過計算機的應該都知道這個協議,它是無狀態的,什麼是無狀態呢?就是說這一次請求和上一次請求是沒有任何關係的,互不認識的,沒有關聯的。但是這種無狀態的的好處是快速。
所以就會帶來一個問題就是,我希望幾個請求的頁面要有關聯,比如:我在a.html裏面登陸了,我在b.html 也希望是登陸狀態,但是,這是2個不同的頁面,也就是2個不同的HTTP請求,這2個HTTP請求是無狀態的,也就是無關聯的,所以無法單純的在a.html中讀取到它在b.html中已經登陸了!那咋搞呢?我不可能這2個頁面我都去登陸一遍吧。或者用笨方法這2個頁面都去查詢數據庫,如果有登陸狀態,就判斷是登陸的了。這種查詢數據庫的方案雖然可行,但是每次都要去查詢數據庫不是個事,會造成數據庫的壓力。
所以正是這種訴求,這個時候,一個新的客戶端存儲數據方式出現了:cookie。cookie是把少量的信息存儲在用戶自己的電腦上,它在一個域名下是一個全局的,只要設置它的存儲路徑在域名www.a.com下 ,那麼當用戶用瀏覽器訪問時,就可以從這個域名的任意頁面讀取cookie中的信息。所以就很好的解決了我在b.html頁面登陸了,b.html獲取到這個登陸信息了。同時又不用反覆去查詢數據庫。雖然這種方案很不錯,也很快速方便,但是由於cookie 是存在用戶端,而且它本身存儲的尺寸大小也有限,最關鍵是用戶可以是可見的(密碼一般不會存於cookie),並可以隨意的修改,很不安全。那如何又要安全,又可以方便的全局讀取信息呢?於是,這個時候,一種新的存儲會話機制:session 誕生了
session的全部機制也是基於這個session_id,它用來區分哪幾次請求是一個人發出的。爲什麼要這樣呢?因爲HTTP是無狀態無關聯的,一個頁面可能會被成百上千人訪問,而且每個人的用戶名是不一樣的,那麼服務器如何區分這次是小王訪問的,那次是小李訪問的呢?所以就有了找個唯一的session_id 來綁定一個用戶。一個用戶在一次會話上就是一個session_id,這樣成千上萬的人訪問,服務器也能區分到底是誰在訪問了。
每次我們訪問一個頁面,默認會自動生成一個session_id 來標註是這次會話的唯一ID,同時也會自動往cookie裏寫入一個名字爲JSESESSID的變量(tomcat生成取名爲jseSessionId),它的值正是session_id,當這次會話沒結束,再次訪問的時候,服務器會去讀取這個JSESESSID的cookie是否有值有沒過期,如果能夠讀取到,則繼續用這個session_id,如果沒有,就會新生成一個session_id,同時生成JSESESSID這個cookie。由於默認生成的這個JSESESSID cookie是會話,也就是說關閉瀏覽器就會過期掉,所以,下次重新瀏覽時,會重新生成一個session_id。
好,這個是session_id,就用來標識綁定一個用戶的,既然session_id生成了。那麼當我們往session裏面寫入數據即可。session是如何保存的?Tomcat保存session的方式是服務器端的內存中。對於PHP而言是保存在文件中。上述有提及。
(一)存儲用戶信息,典型的應用有:
1、購物車功能。(每次加入購物車攜帶session_id傳入到服務器,然後在比較服務器中的session_id(此時一定要session共享),然後重新put新的list到對應的購物車集合中去)
2、登陸狀態(將session存於redis,給其設置過期時間,如果過期了則視爲離線,需重新登陸)
(二)缺陷
①Cookie會被附加在每個HTTP請求中,所以無形中增加了流量。
②由於在HTTP請求中的Cookie是明文傳遞的,所以安全性成問題。(除非用HTTPS)
③Cookie的大小限制在4KB左右。對於複雜的存儲需求來說是不夠用的。
備註
- 除了上述提及的方法外,解決session共享問題還有這樣一種方式:(實質上並不是通過session的共享來解決的)這裏以nginx爲例,將用戶請求分發到了不同機器上,那麼我們只需要固定,同一用戶請求分發到同一機器上進行處理,即這一次用戶來請求服務器了,那麼下一次它再來的時候,同樣也請求也被分發到與上一次相同的服務器。這樣就確保了同一用戶不會因爲請求分發到不同機器上而獲取不到session數據的問題了。
- 從瀏覽器打開訪問了某一個網站,關閉瀏覽器。這樣的操作我們算一次“會話”。所以大部分就會認爲用戶訪問了網站就會產生session ID。實際上不然。例如:在Java中我們需要調用HttpServletRequest的getSession方法創建session。而在PHP中需要
session_start()
一下,服務器纔會將存有session ID的cookie回傳回去。否則不會有什麼session產生。 - session不會因爲瀏覽器的關閉而刪除。但是存有session ID的cookie的默認過期時間是會話級別。也就是用戶關閉了瀏覽器,那麼存儲在客戶端的session ID便會丟失,但是存儲在服務器端的session數據並不會被立即刪除。從客戶端即瀏覽器看來,好像session被刪除了一樣(因爲我們丟失了session ID,找不到原來的session數據了)。
疑問?
當用戶關閉瀏覽器後,該cookie應該就會自動銷燬的。下次用戶再次訪問該網頁,該cookie應該是不復存在了。但是谷歌瀏覽器關閉後,這個PHPSESSID 還存在呢? 比較疑惑,在火狐和IE都測試過,沒有這樣的問題???
參考:https://blog.csdn.net/qq_15096707/article/details/74012116
https://www.cnblogs.com/isme-zjh/p/11359557.html
https://www.zhihu.com/question/19786827/answer/28752144
獲取cookie與session
目前我們項目大多數是基於Spring框架的,所以獲取它只需要拿到HttpServletRequest這個類即可,這個類裏面可以從中拿到前臺的cookie 和 服務器的 session(sessionId) 以及傳入參數
1、RequestContextHolder(org.springframework.web.context.request.RequestContextHolder)
ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = requestAttributes.getRequest();
Cookie[] cookies = request.getCookies();
HttpSession session = request.getSession();
System.out.println("sessionId:" + session.getId());
Enumeration<String> attrs = session.getAttributeNames();
while(attrs.hasMoreElements()){
// 獲取session鍵值
String name = attrs.nextElement().toString();
// 根據鍵值取session中的值
Object vakue = session.getAttribute(name);
System.out.println("------" + name + ":" + vakue +"--------\n");
}
2、ServletActionContext(com.opensymphony.webwork.ServletActionContext)
HttpServletRequest request2 =ServletActionContext.getRequest();
HttpSession session2 = request.getSession();