session 簡介
session 是我們 jsp 九大隱含對象的一個對象。
session 稱作域對象,他的作用是保存一些信息,而 session 這個域對象是一次會話期間使用同一個對象。所以這個對象可以用來保存共享數據。
- 使用 Cookie 有一個非常大的局限,就是如果 Cookie 很多,則無形的增加了客戶端與服務端的數據傳輸量。而且由於瀏覽器對 Cookie 數量的限制,注定我們不能再 Cookie 中保存過多的信息,於是 Session 出現。
- Session 的作用就是在服務器端保存一些用戶的數據,然后傳遞給用戶一個名字為JSESSIONID 的 Cookie,這個 JESSIONID 對應這個服務器中的一個 Session 對象,通過它就可以獲取到保存用戶信息的 Session。
session 是基於 cookie 的。
在用戶第一次使用 session 的時候(訪問 jsp 頁面會獲取 session,所以一般訪問 index.jsp 就算是第一次使用 session 了),服務器會為用戶創建一個 session 域對象。使用 jsessionid 和這個對象關聯,這個對象在整個用戶會話期間使用。響應體增加 set-cookie:jsessionid=xxx 的項。用戶下次以后的請求都會攜帶 jsessionid 這個參數,我們使用 request.getSession()的時候,就會使用 jsessionid 取出 session 對象。
session 原理圖:
HttpSession 的生命周期
什么時候創建 HttpSession 對象
①. 對於 JSP: 是否瀏覽器訪問服務端的任何一個 JSP, 服務器都會立即創建一個 HttpSession 對象呢?
不一定。
- 若當前的 JSP 是客戶端訪問的當前 WEB 應用的第一個資源,且 JSP 的 page 指定的 session 屬性值為 false,則服務器就不會為 JSP 創建一個 HttpSession 對象;
- 若當前 JSP 不是客戶端訪問的當前 WEB 應用的第一個資源,且其他頁面已經創建一個 HttpSession 對象,則服務器也不會為當前 JSP 頁面創建一個 HttpSession 對象,而會把和當前會話關聯的那個 HttpSession 對象返回給當前的 JSP 頁面.
②. 對於 Serlvet: 若 Serlvet 是客戶端訪問的第一個 WEB 應用的資源,則只有調用了 request.getSession() 或 request.getSession(true) 才會創建 HttpSession 對象
page 指令的 session=“false“ 表示什么意思?
當前 JSP 頁面禁用 session 隱含變量!但可以使用其他的顯式的 HttpSession 對象
在 Serlvet 中如何獲取 HttpSession 對象?
request.getSession(boolean create):
create 為 false, 若沒有和當前 JSP 頁面關聯的 HttpSession 對象, 則返回 null; 若有, 則返回 true
create 為 true, 一定返回一個 HttpSession 對象. 若沒有和當前 JSP 頁面關聯的 HttpSession 對象, 則服務器創建一個新的HttpSession 對象返回, 若有, 直接返回關聯的.
request.getSession(): 等同於 request.getSession(true)
什么時候銷毀 HttpSession 對象
①. 直接調用 HttpSession 的 invalidate() 方法: 該方法使 HttpSession 失效
②. 服務器卸載了當前 WEB 應用.
③. 超出 HttpSession 的過期時間.
④. 並不是關閉了瀏覽器就銷毀了 HttpSession.
session 使用
獲取 session 對象
HttpSession session = request.getSession();
session 是我們的四大域對象之一。用來保存數據。常用的方法
session.setAttribute("user", new Object()); session.getAttribute("user"); session.setMaxInactiveInterval(60*60*24);//秒為單位 session.invalidate();//使 session 不可用
Session 時 效
①、基本原則
Session 對象在服務器端不能長期保存,它是有時間限制的,超過一定時間沒有被訪問過的 Session 對象就應該釋放掉,以節約內存。所以 Session 的有效時間並不是從創建對象開始計時,到指定時間后釋放——而是從最后一次被訪問開始計時,統計其“空閑” 的時間。
②、默認設置
在全局 web.xml 中能夠找到如下配置:
<session-config>
<session-timeout>30</session-timeout>
</session-config>
③、手工設置
session.setMaxInactiveInterval(int seconds)
session.getMaxInactiveInterval()
④、強制失效
session.invalidate()
⑤、可以使 Session 對象釋放的情況
Session 對象空閑時間達到了目標設置的最大值,自動釋放
Session 對象被強制失效
Web 應用卸載服務器進程停止
URL 重寫
在整個會話控制技術體系中,保持 JSESSIONID 的值主要通過 Cookie 實現。但 Cookie 在瀏覽器端可能會被禁用,所以我們還需要一些備用的技術手段,例如:URL 重寫。
1)URL 重寫其實就是將 JSESSIONID 的值以固定格式附着在 URL 地址后面,以實現保持
JSESSIONID,進而保持會話狀態。這個固定格式是:URL;jsessionid=xxxxxxxxx
例如:
targetServlet;jsessionid=F9C893D3E77E3E8329FF6BD9B7A09957
2) 實 現 方 式 :
response.encodeURL(String)
response.encodeRedirectURL(String)
例如:
//1.獲取Session對象 HttpSession session = request.getSession(); //2.創建目標URL地址字符串 String url = "targetServlet"; //3.在目標URL地址字符串后面附加JSESSIONID的值 url = response.encodeURL(url); //4.重定向到目標資源 response.sendRedirect(url);
Session 的活化和鈍化
Session 機制很好的解決了 Cookie 的不足,但是當訪問應用的用戶很多時,服務器上就會創建非常多的 Session 對象,如果不對這些 Session 對象進行處理,那么在 Session 失效之前,這些 Session 一直都會在服務器的內存中存在。那么就,就出現了 Session 活化和鈍化的機制。
1)Session 鈍化:
Session 在一段時間內沒有被使用時,會將當前存在的 Session 對象序列化到磁盤上,而不 再 占 用 內 存 空 間 。
2)Session 活化:
Session 被鈍化后,服務器再次調用 Session 對象時,將 Session 對象由磁盤中加載到內存中使用。
如果希望 Session 域中的對象也能夠隨 Session 鈍化過程一起序列化到磁盤上,則對象的實現類也必須實現 java.io.Serializable 接口。不僅如此,如果對象中還包含其他對象的引用,則被關聯的對象也必須支持序列化,否則會拋出異常:java.io.NotSerializableException
表單重復提交問題
什么是表單重復提交?
同一個表單中的數據內容多次提交到服務器。 危害:
服務器重復處理信息,負擔加重。
如果是保存數據可能導致保存多份相同數據。
幾種重復提交
1)提交完表單后,直接刷新頁面,會再次提交。
- 根本原因:Servlet 處理完請求以后,直接轉發到目標頁面。
- 這樣整一個業務,只發送了一次請求,那么當你在瀏覽器中點擊刷新按鈕或者狂按 f5,會一直都會刷新之前的請求
解決方案:使用重定向跳轉到目標頁面
2)提交表單后,由於網速差等原因,服務器還未返回結果,連續點擊提交按鈕,會重 復提交。
- 根本原因:按鈕可以多次點擊
- 解決方案:通過 js,使得按鈕只能提交一次。
$(“#form1”).submit(function(){ $(“#sub_btn”).prop(“disabled”,true); })
3)表單提交后,點擊瀏覽器回退按鈕,不刷新頁面,點擊提交按鈕再次提交表單
- 根本原因:服務器並不能識別請求是否重復。
- 解決方案:使用 token 機制。
1、頁面生成時,產生一個唯一的 token 值。將此值放入 session
2、表單提交時,帶上這個 token 值。
3、服務端驗證 token 值存在,則提交表單,然后移除此值。驗證 token 不存在,說明是之前驗證過一次被移除了,所以是重復請求。不予處理
原理:
代碼:
jsp 頁面
<% String token = System.currentTimeMillis() + ""; request.getSession().setAttribute(token, ""); %> <div> <h1>測試表單重復提交</h1> <form action="login" method="get"> 用戶名:<input name="username" type="text"/> 密碼:<input name="password" type="password"> <input name="token" value="<%=token%>"> <input type="submit"> </form> <hr> </div>
Servlet
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { HttpSession session = request.getSession(); String token = request.getParameter("token"); Object attribute = session.getAttribute(token); response.setContentType("text/html;charset=UTF-8"); if(attribute!=null){ session.removeAttribute(token); response.getWriter().write("請求成功!"); }else{ response.getWriter().write("請不要重復請求!"); } }
其實防止重復提交的核心就是讓服務器有一個字段能來識別此次請求是否已經執行。 這個字段需要頁面傳遞過來,因為只要回退回去的頁面,字段都是一致的。不會變化, 通過這個特性我們想到了 token 機制來防止重復提交