問題引出
解決方法1- 使用cookie
在使用者登入的時候,伺服器可以將使用者資訊通過cookie的形式保留在瀏覽器。每當使用者存取不同的網頁(傳送HTTP請求),瀏覽器都會將該cookie傳送給伺服器,伺服器通過獲取cookie的值,在網頁上就可以顯示當前使用者的資訊;同時伺服器也可以通過cookie(使用者資訊)找到使用者操作使用者在資料庫中對應的資料。
但是使用cookie會存在以下問題:第一是cookie不能存放大的資料;第二是cookie不安全,不能存放敏感資訊;第三,cookie不是跟一個使用者關聯的。如果一個cookie是長期儲存的,那麼當其他人開啟瀏覽器時,也可以登入你的賬號,因此單單使用cookie來實現也不太理想。
解決方法2- 使用session
session是伺服器端的技術,也就是說session的資料是儲存在伺服器端的。伺服器在執行時會為每一個使用者的瀏覽器建立一個其獨享的session物件(該物件可以理解為一個集合)。
由於session為每個使用者瀏覽器獨享,所以使用者在存取伺服器的不同頁面時,可以從各自的session中讀取/新增資料,從而完成任務。
當用戶開啟瀏覽器,存取某個網站,操作session時,伺服器就會在記憶體(在伺服器端)為該瀏覽器分配一個session物件,該session物件被這個瀏覽器獨佔,如上圖
這個session物件也可以看做是一個容器/集合,session物件預設存在的時間為30min(在tomcat/conf/web.xml中可以修改)
session儲存結構示意圖
你可以把session看做是一容器(類似HashMap),有兩列(k-v),每一行就是session的一個屬性
每個屬性包含有兩個部分,一個是該屬性的名字(String),一個是該屬性的值(Object)
建立和獲取session(api一樣)
HttpSession session = request.getSession();
第一次呼叫是建立Session對談,之後呼叫是獲取建立好的Session物件
向session新增屬性
session.setAttribute(String name,Object val);
從session得到某個屬性
Object obj = session.getAttribute(String name);
從session刪除某個屬性
session.removeAttribute(String name);
isNew(); 判斷是不是剛建立出來的Session
每個Session都有一個唯一標識的Id值(即JSESSIONID),通過getId()得到Session的對談Id值
整個過程如下:
一個瀏覽器向伺服器傳送請求,要操作session時,一定會呼叫request.getSession()方法。
如果伺服器在本次對談中,建立了session,則在響應頭中將以Set-Cookie:JSESSIONID=xxxx
的形式返回一個cookie給瀏覽器儲存。下一次瀏覽器傳送請求時,伺服器就可以拿到cookie中的JSESSIONID的值,在map中找到該瀏覽器對應的session,直接操作。
這裡的map可以理解為在Tomcat中還維護了一個容器HashMap<String,HttpSession>,這個容器中以JSESSIONID為key,以session為value,完成兩者的繫結。
演示Session底層實現機制-建立和讀取Session
web.xml:
<servlet>
<servlet-name>CreateSession</servlet-name>
<servlet-class>com.li.session.CreateSession</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>CreateSession</servlet-name>
<url-pattern>/createSession</url-pattern>
</servlet-mapping>
CreateSession:
package com.li.session;
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.IOException;
import java.io.PrintWriter;
public class CreateSession extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request, response);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//System.out.println("CreateSession doPost被呼叫...");
//1.獲取session(同時也可能建立session)
HttpSession session = request.getSession();//注意這個地方已經把sessionId分配了
//2.給session獲取id
System.out.println("當前sessionid= " + session.getId());
//3.給session存放一些資料
session.setAttribute("email", "[email protected]");
//4.給瀏覽器傳送一個回覆
response.setContentType("text/html;charset=utf-8");
PrintWriter writer = response.getWriter();
writer.print("<h1>建立/操作session成功...</h1>");
writer.flush();
writer.close();
}
}
首先存取http://localhost:8080/cs/createSession
:
抓包顯示:
走的是如下流程:
伺服器後端顯示:
此時瀏覽器儲存的JSESSIONID變成了新的值:
此時,如果再次存取伺服器,就會攜帶cookie中新的jsessionid給伺服器,伺服器不再返回jsessionid。
走的是如下流程:
如果redeployTomcat,關閉瀏覽器並重新開啟,直接存取http://localhost:8080/cs/createSession
,這時候的請求頭將不會攜帶jsessionid(因為關閉瀏覽器時預設刪除了cookie),伺服器返回的響應頭將會攜帶一個jsessionid(因為重新發布tomcat,會清空伺服器記憶體,這時請求的資源createSession程式會建立一個session,因此伺服器會返回一個與之關聯的jsessionid)
這時候走的就是如下流程:
web.xml:
<servlet>
<servlet-name>ReadSession</servlet-name>
<servlet-class>com.li.session.ReadSession</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>ReadSession</servlet-name>
<url-pattern>/readSession</url-pattern>
</servlet-mapping>
ReadSession:
package com.li.session;
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.IOException;
import java.io.PrintWriter;
public class ReadSession extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request, response);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//System.out.println("ReadSession doPost被呼叫...");
//演示讀取session
//1.獲取session,如果沒有session也會建立
HttpSession session = request.getSession();
//2.給session獲取id
System.out.println("ReadSession 當前sessionid= " + session.getId());
//3.讀取屬性
Object email = session.getAttribute("email");
if (email != null) {
System.out.println("session屬性 email= " + (String) email);
} else {
System.out.println("session中沒有 email屬性");
}
//3.給瀏覽器傳送一個回覆
response.setContentType("text/html;charset=utf-8");
PrintWriter writer = response.getWriter();
writer.print("<h1>讀取session成功...</h1>");
writer.flush();
writer.close();
}
}
redeployTomcat,首先在瀏覽器中存取http://localhost:8080/cs/createSession
(目的是建立一個和瀏覽器關聯的session,因為redeployTomcat會刪除伺服器的session)
然後存取http://localhost:8080/cs/readSession
,並抓包:
在伺服器後臺輸出如下:第一行輸出是我們建立session時獲取的sessionid,第二行是我們讀取session時獲取的sessionid,並獲取其屬性。
public void setMaxInactiveInterval(int interval)
:設定session的超時時間(以秒為單位),超過指定的時長,session就會被銷燬。
值為正數的時候,設定session的超時時長。
值為負數時,表示永不超時
public int getMaxInactiveInterval()
表示獲取session的超時時間
public void invalidate()
表示讓當前的session對談立即無效
如果沒有呼叫setMaxInactiveInterval(int interval)
來指定session的生命時長,Tomcat會以session的預設時長為準,session的預設時長為30分鐘,可以在tomcat目錄的conf目錄下的web.xml中設定。
Session的生命週期指的是:使用者端兩次請求的最大間隔時長,而不是累積時長。即當用戶端存取了自己的session,session的生命週期將將從0開始重新計算。(指的是同一個對談兩次請求之間的間隔時間)
cookie的生命週期指的是累積時長
底層:Tomcat用一個執行緒來輪詢對談狀態,如果某個對談的空閒時間超過設定的最大值,則將該對談銷燬。