ession 簡介
session 是我們 jsp 九大隱含對象的一個對象。
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 對象呢?
不一定。
②. 對于 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 機制來防止重復提交
生苦短,我用 Python
如果我的文章對您有幫助,請關注支持下作者的公眾號:極客挖掘機,您的關注,是對小編堅持原創的最大鼓勵:)
前文傳送門:
小白學 Python 爬蟲(1):開篇
小白學 Python 爬蟲(2):前置準備(一)基本類庫的安裝
小白學 Python 爬蟲(3):前置準備(二)Linux基礎入門
小白學 Python 爬蟲(4):前置準備(三)Docker基礎入門
小白學 Python 爬蟲(5):前置準備(四)數據庫基礎
小白學 Python 爬蟲(6):前置準備(五)爬蟲框架的安裝
小白學 Python 爬蟲(7):HTTP 基礎
小白學 Python 爬蟲(8):網頁基礎
小白學 Python 爬蟲(9):爬蟲基礎
先說一個題外話,今天老司機翻車了,內容小編今天來不及寫了,后面會整理下,分享給大家。
在介紹 Session 和 Cookies 之前,先介紹一個另外的概念 —— 靜態網頁和動態網頁。
靜態網頁就是我們上一篇寫的那種 html 頁面,后綴為 .html 的這種文件,直接部署到或者是放到某個 web 容器上,就可以在瀏覽器通過鏈接直接訪問到了,常用的 web 容器有 Nginx 、 Apache 、 Tomcat 、Weblogic 、 Jboss 、 Resin 等等,很多很多。
如果說要舉例子的話那么小編的個人博客站:https://www.geekdigging.com/ 就是一個純粹的靜態網頁。
這種網頁的內容是通過純粹的 HTML 代碼來書寫,包括一些資源文件:圖片、視頻等內容的引入都是使用 HTML 標簽來完成的。
它的好處當然是加載速度快,編寫簡單,訪問的時候對 web 容器基本上不會產生什么壓力。但是缺點也很明顯,可維護性比較差,不能根據參數動態的顯示內容等等。
有需求就會有發展么,這時動態網頁就應運而生了。
我們先不說動態網頁的概念,先說說有哪些網站是由動態網頁來構建的。大家常用的某寶、某東、拼夕夕等網站都是由動態網頁組成的。
動態網頁可以解析 URL 中的參數,或者是關聯數據庫中的數據,顯示不同的網頁內容。
現在各位同學訪問的網站大多數都是動態網站,它們不再簡簡單單是由 HTML 堆砌而成,可能是由 JSP 、 PHP 等語言編寫的,當然,現在很多由前端框架編寫而成的網頁小編這里也歸屬為動態網頁。
說到動態網頁,各位同學可能使用頻率最高的一個功能是登錄,像各種電商類網站,肯定是登錄了以后才能下單買東西。那么,問題來了,后面的服務端是如何知道當前這個人已經登錄了呢?
現在大多數的網站使用的協議都是 HTTP/1.1 ,而 HTTP/1.1 最大的特點就是無狀態、無連接的。
無狀態就是指 HTTP 協議對于請求的發送處理是沒有記憶功能的,也就是說每次 HTTP 請求到達服務端,服務端都不知道當前的客戶端(瀏覽器)到底是一個什么狀態。客戶端向服務端發送請求后,服務端處理這個請求,然后將內容響應回客戶端,完成一次交互,這個過程是完全相互獨立的,服務端不會記錄前后的狀態變化,也就是缺少狀態記錄。
這就產生了上面的問題,服務端如何知道當前在瀏覽器面前操作的這個人是誰?
其實,在用戶做登錄操作的時候,服務端會下發一個類似于 token 憑證的東西返回至客戶端(瀏覽器),有了這個憑證,才能保持登錄狀態。
那么這個憑證是什么?
這就是本篇文章要解釋的核心內容,Session 和 Cookies 了。
Session 是會話的意思,會話是產生在服務端的,用來保存當前用戶的會話信息,而 Cookies 是保存在客戶端(瀏覽器),有了 Cookie 以后,客戶端(瀏覽器)再次訪問服務端的時候,會將這個 Cookie 帶上,這時,服務端可以通過 Cookie 來識別本次請求到底是誰在訪問。
可以簡單理解為 Cookies 中保存了登錄憑證,我們只要持有這個憑證,就可以在服務端保持一個登錄狀態。
在爬蟲中,有時候遇到需要登錄才能訪問的網頁,只需要在登錄后獲取了 Cookies ,在下次訪問的時候將登錄后獲取到的 Cookies 放在請求頭中,這時,服務端就會認為我們的爬蟲是一個正常登錄用戶。
那么,Cookies 是如何保持會話狀態的呢?
在客戶端(瀏覽器)第一次請求服務端的時候,服務端會返回一個請求頭中帶有 Set-Cookie 字段的響應給客戶端(瀏覽器),用來標記是哪一個用戶,客戶端(瀏覽器)會把這個 Cookies 給保存起來。
我們來使用工具 PostMan 來訪問下某東的登錄頁,看下返回的響應頭:
當我們輸入好用戶名和密碼時,客戶端會將這個 Cookies 放在請求頭一起發送給服務端,這時,服務端就知道是誰在進行登錄操作,并且可以判斷這個人輸入的用戶名和密碼對不對,如果輸入正確,則在服務端的 Session 記錄一下這個人已經登錄成功了,下次再請求的時候這個人就是登錄狀態了。
如果客戶端傳給服務端的 Cookies 是無效的,或者這個 Cookies 根本不是由這個服務端下發的,或者這個 Cookies 已經過期了,那么接下里的請求將不再能訪問需要登錄后才能訪問的頁面。
所以, Session 和 Cookies 之間是需要相互配合的,一個在服務端,一個在客戶端。
我們還是打開某東的網站,看下這些 Cookies到底有哪些內容:
具體操作方式還是在 Chrome 中按 F12 打開開發者工具,選擇 Application 標簽,點開 Cookies 這一欄。
那么有的網站為什么這次關閉了,下次打開的時候還是登錄狀態呢?
這就要說到 Cookie 的持久化了,其實也不能說是持久化,就是 Cookie 失效的時間設置的長一點,比如直接設置到 2099 年失效,這樣,在瀏覽器關閉后,這個 Cookie 是會保存在我們的硬盤中的,下次打開瀏覽器,會再從我們的硬盤中將這個 Cookie 讀取出來,用來維持用戶的會話狀態。
第二個問題產生了,服務端的會話也會無限的維持下去么,當然不會,這就要在 Cookie 和 Session 上做文章了, Cookie 中可以使用加密的方式將用戶名記錄下來,在下次將 Cookies 讀取出來由請求發送到服務端后,服務端悄悄的自己創建一個用戶已經登錄的會話,這樣我們在客戶端看起來就好像這個登錄會話是一直保持的。
當我們關閉瀏覽器的時候會自動銷毀服務端的會話,這個是錯誤的,因為在關閉瀏覽器的時候,瀏覽器并不會額外的通知服務端說,我要關閉了,你把和我的會話銷毀掉吧。
因為服務端的會話是保存在內存中的,雖然一個會話不會很大,但是架不住會話多啊,硬件畢竟是會有限制的,不能無限擴充下去的,所以在服務端設置會話的過期時間就非常有必要。
當然,有沒有方式能讓瀏覽器在關閉的時候同步的關閉服務端的會話,當然是可以的,我們可以通過腳本語言 JS 來監聽瀏覽器關閉的動作,當瀏覽器觸發關閉動作的時候,由 JS 像服務端發起一個請求來通知服務端銷毀會話。
由于不同的瀏覽器對 JS 事件的實現機制不一致,不一定保證 JS 能監聽到瀏覽器關閉的動作,所以現在常用的方式還是在服務端自己設置會話的過期時間。
https://baike.baidu.com/item/cookie/1119
建工程
File>New>Web Project
填寫項目名稱Project Name,舉例為andylife,這里使用的是默認存儲路徑,如需修改,把下面Use default location前面的勾去掉,并在Directory填寫自己的地址
設置完成后點擊Finish,新建完成
編寫內容
打開WebRoot目錄下的index.jsp,把編碼改為utf-8
網頁修改內容,顯示日期時間源碼:
<%@ page language="java" import="java.util.*" pageEncoding="utf-8"%>
<%
String path=request.getContextPath();
String basePath=request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>
<html>
<body>
<%
//設置刷新頁面的時間,每隔1秒鐘刷新一次
response.setHeader("refresh", "10");
%>
當前的系統時間是:
<%
//輸出當前時間
Calendar now=Calendar.getInstance();
out.print(now.get(Calendar.YEAR) + "年");
out.print(now.get(Calendar.MONTH) + 1 + "月");
out.print(now.get(Calendar.DATE) + "日");
out.print(now.get(Calendar.HOUR_OF_DAY) + "時");
out.print(now.get(Calendar.MINUTE) + "分");
out.print(now.get(Calendar.SECOND) + "秒 ");
out.print("星期");
switch (now.get(Calendar.DAY_OF_WEEK)) {
case 1:
out.print("日");
break;
case 2:
out.print("一");
break;
case 3:
out.print("二");
break;
case 4:
out.print("三");
break;
case 5:
out.print("四");
break;
case 6:
out.print("五");
break;
case 7:
out.print("六");
}
out.print(""+"<hr>");
out.print("ID Things need to be done IF DAILY "+"<br>");
%>
</body>
<body>
<%
out.print(""+"<hr>");
%>
</body>
</html>
運行
把頁面部署到Tomcat上
點擊ADD,然后選擇我們的Tomcat服務器,點擊Finish,然后點OK
點擊旁邊圖標的小三角形,選擇服務器,并運行
當我們在控制臺看到此信息時,服務器就開啟成功了
前端瀏覽
在瀏覽器輸入網址:http://localhost:8080/life/index.jsp
它表示的是,在本地8080端口下,life中的index.jsp
*請認真填寫需求信息,我們會在24小時內與您取得聯系。