1.什麼叫會話
一次會話指的是:就好比打電話,A給B打電話,接通之後,會話開始,直到掛斷電話,該次會話就結束了,而瀏覽器訪問服務器,就跟打電話一樣,瀏覽器A給服務器發送請求,訪問web程序,該次會話就已經接通,其中不管瀏覽器發送多少請求(就相當於接通電話後說話一樣),都視爲一次會話,直到瀏覽器關閉,本次會話結束。
其中注意,一個瀏覽器就相當於一部電話,如果使用火狐瀏覽器,訪問服務器,就是一次會話了,然後打開google瀏覽器,訪問服務器,這是另一個會話,雖然是在同一臺電腦,同一個用戶在訪問,但是,這是兩次不同的會話。
2.引入cookie和session
思考一個問題,一個瀏覽器訪問一個服務器就能建立一個會話,如果別的電腦,都同時訪問該服務器,就會創建很多會話,就拿一些購物網站來說,我們訪問一個購物網站的服務器,會話就被創建了,然後就點擊瀏覽商品,對感興趣的商品就先加入購物車,等待一起付賬,這看起來是很普通的操作,但是想一下,如果有很多別的電腦上的瀏覽器同時也在訪問該購物網站的服務器,跟我們做類似的操作呢?服務器又是怎麼記住用戶,怎麼知道用戶A購買的任何商品都應該放在A的購物車內,不論是用戶A什麼時間購買的,不能放入用戶B或用戶C的購物車內的呢?
這裏我們就用cookie和session兩種會話跟蹤技術來跟蹤整個會話。
3.cookie簡介
3.1.cookie的工作原理
1)首先瀏覽器向服務器發出請求。
2)服務器就會根據需要生成一個Cookie對象,並且把數據保存在該對象內。
3)然後把該Cookie對象放在響應頭,一併發送回瀏覽器。
4)瀏覽器接收服務器響應後,提出該Cookie保存在瀏覽器端。
5)當下一次瀏覽器再次訪問那個服務器,就會把這個Cookie放在請求頭內一併發給服務器。
6) 服務器從請求頭提取出該Cookie,判別裏面的數據,然後作出相應的動作。
3.2.cookie中的常用方法
Cookie cookie=new Cookie(String name,String value) 構造一個cookie對象
response.addCookie(Cookie cookie) 是將一個cookie對象傳入客戶端。
request.getCookies() 得到所有的cookie對象
cookie.getName() 得到此cookie對象的名字
cookie.getValue() 得到對應名稱的cookie的值
cookie.setMaxAge() 設置過期時間
3.3.案例
@WebServlet("/CookieServletDemo1")
public class CookieServletDemo1 extends HttpServlet {
private static final long serialVersionUID = 1L;
/**
* @see HttpServlet#HttpServlet()
*/
public CookieServletDemo1() {
super();
// TODO Auto-generated constructor stub
}
/**
* @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse
* response)
*/
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 創建cookie對象
// cookie中`存放的數據以鍵值對存在map<String,String> 鍵和值都只能是字符串,不支持中文
Cookie cookie = new Cookie("username", "zhangsan");
Cookie cookie1 = new Cookie("password", "1234");
// 失效時間 以秒爲單位
cookie.setMaxAge(60 * 60);
response.addCookie(cookie);
response.addCookie(cookie1);
}
/**
* @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse
* response)
*/
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// TODO Auto-generated method stub
doGet(request, response);
}
}
@WebServlet("/CookieServletDemo2")
public class CookieServletDemo2 extends HttpServlet {
private static final long serialVersionUID = 1L;
/**
* @see HttpServlet#HttpServlet()
*/
public CookieServletDemo2() {
super();
// TODO Auto-generated constructor stub
}
/**
* @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse
* response)
*/
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 從請求頭中獲取cookie信息
Cookie[] cookies = request.getCookies();
for(Cookie c:cookies){
System.out.println(c.getName()+"===="+c.getValue());
}
}
/**
* @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse
* response)
*/
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// TODO Auto-generated method stub
doGet(request, response);
}
}
3.4.瀏覽器中查看cookie
不同的瀏覽器略有差別,這裏以谷歌瀏覽器爲例。
F12打開開發者工具 ---點擊Application選項---選中其中的cookie---選擇相應的站點---看到該站點中的所有cookie信息。
3.5.cookie的應用場景
cookie的應用場景有很多,最具代表性的當屬網站的記錄用戶賬號和密碼的功能了,大家可能經常看到登錄某某論壇,某某網站時,下面有個選項爲N天內自動登錄,其實這就是cookie的應用。當用戶第一次輸入賬號密碼時給服務器發送請求時,服務器會根據賬號密碼回寫一個字符串cookie,當用戶下次再向該服務器發送登錄請求時,則帶着這個字符串cookie一起去訪問服務器,這時,服務器只需要對比次字符串和數據庫中存儲的字符串是否相同,則可以達到用戶自動登錄功能。
3.6.cookie的侷限性
- Cookie數量和長度的限制。每個站點最多只能有20條cookie,每個cookie長度不能超過4KB,否則會被截掉。
- cookie中只能存字符串。且不支持中文
- cookie不適合保存敏感數據(例如密碼)可見的
4.session
4.1.session的工作原理
1)瀏覽器發出請求到服務器。
2)服務器會根據需求生成Session對象,並且給這個Session對象一個編號,一個編號對應一個Session對象
3)服務器把需要記錄的數據封裝到這個Session對象裏,然後把這個Session對象保存下來。
4)服務器把這個Session對象的編號放到一個Cookie裏,隨着響應發送給瀏覽器
5)瀏覽器接收到這個cookie就會保存下來
6)當下一次瀏覽器再次請求該服務器服務,就會發送該Cookie
7)服務器得到這個Cookie,取出它的內容,它的內容就是一個Session的編號!!!
8)憑藉這個Session編號找到對應的Session對象,然後利用該Session對象把保存的數據取出來!
4.2.session的常用方法
方法 | 解釋 |
---|---|
void setAttribute(String attribute, Object value) | 設置Session屬性。value參數可以爲任何Java Object。通常爲Java Bean。value信息不宜過大 |
String getAttribute(String attribute) | 返回Session屬性 |
void removeAttribute(String attribute) | 移除Session屬性 |
String getId() | 返回Session的ID。該ID由服務器自動創建,不會重複 |
long getCreationTime() | 返回Session的創建日期。 |
long getLastAccessedTime() | 返回Session的最後活躍時間。返回類型爲long |
int getMaxInactiveInterval() | 返回Session的超時時間。單位爲秒。超過該時間沒有訪問,服務器認爲該Session失效 |
void setMaxInactiveInterval(int second) | 設置Session的超時時間。單位爲秒4.3. |
4.3.案例
@WebServlet("/SeesionServletDemo1")
public class SeesionServletDemo1 extends HttpServlet {
private static final long serialVersionUID = 1L;
/**
* @see HttpServlet#HttpServlet()
*/
public SeesionServletDemo1() {
super();
// TODO Auto-generated constructor stub
}
/**
* @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse
* response)
*/
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 創建session對象
HttpSession session = request.getSession();
System.out.println(session.getId());
session.setAttribute("name", "zhangsan");
session.setAttribute("student", new Student(1, "zhangsan", 12));
Student stu = (Student) session.getAttribute("student");
stu.setName("lisi");
// 設置過期時間 單位是秒
// session.setMaxInactiveInterval(2);
// 直接銷燬session 註銷登錄
// session.invalidate();
}
/**
* @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse
* response)
*/
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// TODO Auto-generated method stub
doGet(request, response);
}
}
public class Student {
private Integer id;
private String name;
private Integer age;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public Student(Integer id, String name, Integer age) {
super();
this.id = id;
this.name = name;
this.age = age;
}
public Student() {
super();
// TODO Auto-generated constructor stub
}
}
@WebServlet("/SeesionServletDemo2")
public class SeesionServletDemo2 extends HttpServlet {
private static final long serialVersionUID = 1L;
/**
* @see HttpServlet#HttpServlet()
*/
public SeesionServletDemo2() {
super();
// TODO Auto-generated constructor stub
}
/**
* @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse
* response)
*/
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 創建session對象
HttpSession session = request.getSession();
System.out.println(session.getAttribute("name"));
Student stu = (Student) session.getAttribute("student");
System.out.println(stu.getName());
}
/**
* @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse
* response)
*/
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// TODO Auto-generated method stub
doGet(request, response);
}
}
4.4.session的生命週期
session對象生命週期:
- session對象什麼創建?
- 執行request.getSession()方法時
- session對象什麼銷燬?
- 默認情況下,session對象在30分鐘之後服務器自動銷燬。
- 手動設置session有效時長
- void setMaxInactiveInterval(int interval) -以秒爲單位。
- 手動銷燬
- void invalidate()
4.5.session在一次會話結束後消失的原因
由於session的使用需要依賴cookie,cookie每次從瀏覽器端傳輸session的id到後臺,然後查找對應編號的session進行使用,但由於此時的cookie默認的失效時間是一次會話,當一次會話結束後,存放id的cookie對象就消失了,下一次會話訪問時就會生成新的id,那存儲在原來的session對象中的數據就無法找到了。
4.6.瀏覽器禁用cookie能否使用session
可以,但需要手動拼接id傳過去,例如
http://localhost:8080/day06/session.jsp;jsessionid=AE62ECBAAD2CA16DA6AEBF1D1527CD45
jsessionid指的就是session對應的id
4.7.session共享
4.7.1.基於數據庫的Session共享
首選當然是大名鼎鼎的Mysql數據庫,並且建議使用內存表Heap,提高session操作的讀寫效率。這個方案的實用性比較強,相信大家普遍在使用,它的缺點在於session的併發讀寫能力取決於Mysql數據庫的性能,同時需要自己實現session淘汰邏輯,以便定時從數據表中更新、刪除 session記錄,當併發過高時容易出現表鎖,雖然我們可以選擇行級鎖的表引擎,但不得不否認使用數據庫存儲Session還是有些殺雞用牛刀的架勢。
4.7.2.基於Cookie的Session共享
這個方案我們可能比較陌生,但它在大型網站中還是比較普遍被使用。原理是將全站用戶的Session信息加密、序列化後以Cookie的方式, 統一種植在根域名下(如:.host.com),利用瀏覽器訪問該根域名下的所有二級域名站點時,會傳遞與之域名對應的所有Cookie內容的特性,從而實現 用戶的Cookie化Session 在多服務間的共享訪問。
這個方案的優點無需額外的服務器資源;缺點是由於受http協議頭信心長度的限制,僅能夠存儲小部分的用戶信息,同時Cookie化的 Session內容需要進行安全加解密(如:採用DES、RSA等進行明文加解密;再由MD5、SHA-1等算法進行防僞認證),另外它也會佔用一定的帶寬資源,因爲瀏覽器會在請求當前域名下任何資源時將本地Cookie附加在http頭中傳遞到服務器。
4.7.3.基於Memcache的Session共享
Memcache由於是一款基於Libevent多路異步I/O技術的內存共享系統,簡單的Key + Value數據存儲模式使得代碼邏輯小巧高效,因此在併發處理能力上佔據了絕對優勢,目前本人所經歷的項目達到2000/秒 平均查詢,並且服務器CPU消耗依然不到10%。
另外值得一提的是Memcache的內存hash表所特有的Expires數據過期淘汰機制,正好和Session的過期機制不謀而合,降低了 過期Session數據刪除的代碼複雜度,對比“基於數據庫的存儲方案”,僅這塊邏輯就給數據表產生巨大的查詢壓力。