問題引入
使用Java(包含但不僅限)進行web開發,採用http作爲前後端交互的協議,那麼這裏需要明白一下HTTP是無狀態的。簡單來講,使用HTTP協議訪問服務器時,每次訪問之間是互不影響的,這就是無狀態。
但是,存在一個問題,服務器需要保存用戶訪問的一些狀態。大家想一下,爲什麼每次登陸淘寶和京東之後,系統會知道我是我呢?原因就是服務器保存了你的登陸信息,所以當你再次訪問服務器的時候系統就知道你是你了。
由於Http協議是無狀態的,也就導致服務器無法分辨是誰瀏覽了網頁。爲維持用戶在網站的狀態,比如登陸、購物車等,先後出現了四種技術,分別是隱藏表單域、URL重寫、cookie、session。
session和cookie簡單說明
本質上這兩個技術(概念),其目的就是爲了在保存信息(狀態)
session:解決Http協議不能維持狀態的問題,但是session只存儲在服務器端的,不會在網絡中進行傳輸。[服務端狀態保存技術]
cookie:是瀏覽器保存在用戶電腦上的一小段文本,用來保存用戶在網站上的必要的信息。[客戶端狀態保存技術]
通過登陸瞭解cookie和session
1.用戶在登錄時向服務器發送HTTP請求,攜帶username和password;
2.服務器接受請求,執行登陸邏輯,生成用戶session保存用戶登陸信息,同時服務器端生成對應的cookie,該cookie中包含了sessionID,響應給瀏覽器;
2.瀏覽器本地保存cookie值;
3.登錄後,用戶再向服務器發送請求到服務器,則帶着瀏覽器本地保存的cookie值;
4.服務器在接受到請求後,獲取到請求參數(裏面帶有cookie),服務器從cookie值取出sessionID,根據sessionID找到對應session,若有對應的session,則可以執行相應的操作,否者需要進行其他處理。
大家可以看一幅如下圖理解一下:
session和cookie有啥區別?
- cookie和session都是基於鍵值對的字符串,都由後端服務器生成;
- cookie保存在客戶端瀏覽器中,session保存在服務器中,session比cookie更安全。
- session是key-value格式的數據,則它的鍵session_id保存在cookie中的,值session_data保存在服務器,通過key來取到value;
cookie機制
S(服務端)—>C(客戶端) 生成cookie
正統的cookie分發是通過擴展HTTP協議來實現的,服務器通過在HTTP的響應頭中加上一行特殊的指示以提示瀏覽器按照指示生成相應的cookie。
C(客戶端) —>S(服務端)傳遞cookie
而cookie 的使用是由瀏覽器按照一定的原則在後臺自動發送給服務器的。瀏覽器檢查所有存儲的cookie,如果某個cookie所聲明的作用範圍大於等於將要請求的資源所在的位置,則把該cookie附在請求資源的HTTP請求頭上發送給服務器。
cookie的內容主要包括:名字,值,過期時間,路徑和域。
域(域名)
例如:.csdb.net,或者mp.csdn.net
路徑
就是跟在域名後面的URL路徑,例如:/或者/foo等等,路徑與域合在一起就構成了cookie的作用範圍。
過期時間
如果不設置過期時間,則表示這個cookie的生命期爲瀏覽器會話期間,只要關閉瀏覽器窗口,cookie就消失了。這種生命期爲瀏覽器會話期的 cookie被稱爲會話cookie。會話cookie一般不存儲在硬盤上而是保存在內存裏,當然這種行爲並不是規範規定的。
如果設置了過期時間,瀏覽器就會把cookie保存到硬盤上,關閉後再次打開瀏覽器,這些cookie仍然有效直到超過設定的過期時間。
下面就是一個goolge設置cookie的響應頭的例子
HTTP/1.1 302 Found
Location: http://www.google.com/intl/zh-CN/
// Set-Cookie提示瀏覽器按照指示生成相應的cookie
Set-Cookie:PREF=ID=0565f77e132de138:NW=1:TM=1098082649:LM=1098082649:S=KaeaCFPo49RiA_d8; expires=Sun,
17-Jan-2038 19:14:07 GMT; path=/; domain=.google.com
Content-Type: text/html
瀏覽器在再次訪問goolge的資源時自動向外發送cookie
session機制
session機制是一種服務器端狀態保存技術,服務器使用一種類似於散列表的結構來保存信息。
session創建
- 當程序需要爲某個客戶端的請求創建一個session的時候,服務器首先檢查這個客戶端的請求裏是否已包含了一個session標識 - 稱爲session id;
- 如果已包含一個session id則說明以前已經爲此客戶端創建過session,服務器就按照session如果已包含一個session id則說明以前已經爲此客戶端創建過session,服務器就按照session id把這個 session檢索出來使用(如果檢索不到,可能會新建一個);
- 如果客戶端請求不包含session如果客戶端請求不包含session id,則爲此客戶端創建一個session並且生成一個與此session相關聯的session id;
- session id的值應該是一個既不會重複,又不容易被找到規律以仿造的字符串,這個 session id將被在本次響應中返回給客戶端保存。
sessionID 保存
cookie
保存這個session id的方式可以採用cookie,這樣在交互過程中瀏覽器可以自動的按照規則把這個標識返回給服務器。一般這個cookie的名字都是類似於SEEESIONID,而。比如weblogic對於web應用程序生成的cookie,JSESSIONID= yOK3vjFD75aPnrF7C2HmdnV6QZcEbzWoWiBYEnLerjQ99zWpBng!-145788764,它的名字就是 JSESSIONID。
URL重寫
由於cookie可以被人爲的禁止,必須有其他機制以便在cookie被禁止時仍然能夠把session id傳遞迴服務器。經常被使用的一種技術叫做URL重寫,就是把session id直接附加在URL路徑的後面,附加方式也有兩種:
- 一種是作爲URL路徑的附加信息,表現形式爲http://…/xxx;jsessionid=ByOK3vjFD75aPnrF7C2HmdnV6QZcEbzWoWiBYEnLerjQ99zWpBng!-145788764
- 另一種是作爲查詢字符串附加在URL後面,表現形式爲http://…/xxx?jsessionid=ByOK3vjFD75aPnrF7C2HmdnV6QZcEbzWoWiBYEnLerjQ99zWpBng!-145788764
這兩種方式對於用戶來說是沒有區別的,只是服務器在解析的時候處理的方式不同,採用第一種方式也有利於把session id的信息和正常程序參數區分開來。爲了在整個交互過程中始終保持狀態,就必須在每個客戶端可能請求的路徑後面都包含這個session id。
表單隱藏字段
就是服務器會自動修改表單,添加一個隱藏字段,以便在表單提交時能夠把session id傳遞迴服務器。比如下面的表單
<form name="testform" action="/xxx">
<input type="text">
</form>
在被傳遞給客戶端之前將被改寫成
<form name="testform" action="/xxx">
<input type="hidden" name="jsessionid" value="ByOK3vjFD75aPnrF7C2HmdnV6QZcEbzWoWiBYEnLerjQ99zWpBng!-145788764">
<input type="text">
</form>
一個誤解
常常聽到這樣一種誤解“只要關閉瀏覽器,session就消失了”,這是有問題的。
除非程序通知服務器刪除一個session,否則服務器會一直保留,程序一般都是在用戶做log off的時候發個指令去刪除session。
然而瀏覽器從來不會主動在關閉之前通知服務器它將要關閉,因此服務器根本不會有機會知道瀏覽器已經關閉,之所以會有這種錯覺,是大部分session機制都使用會話cookie來保存session id,而關閉瀏覽器後這個 session id就消失了,再次連接服務器時也就無法找到原來的session。如果服務器設置的cookie被保存到硬盤上,或者使用某種手段改寫瀏覽器發出的HTTP請求頭,把原來的session id發送給服務器,則再次打開瀏覽器仍然能夠找到原來的session。
恰恰是由於關閉瀏覽器不會導致session被刪除,迫使服務器爲seesion設置了一個失效時間,當距離客戶端上一次使用session的時間超過這個失效時間時,服務器就可以認爲客戶端已經停止了活動,纔會把session刪除以節省存儲空間。
常見的問題
session問題
1、session何時被創建
一個常見的誤解是以爲session在有客戶端訪問時就被創建,然而事實是直到某server端程序調用HttpServletRequest.getSession(true)
這樣的語句時才被創建,注意如果JSP沒有顯示的使用 <% @page session=“false”%>關閉session,則JSP文件在編譯成Servlet時將會自動加上這樣一條語句HttpSession session = HttpServletRequest.getSession(true);
這也是JSP中隱含的 session對象的來歷。由於session會消耗內存資源,因此,如果不打算使用session,應該在所有的JSP中關閉它。
2、session何時被刪除
綜合前面的討論,session在下列情況下被刪除
a.程序調用HttpSession.invalidate();
b.距離上一次收到客戶端發送的session id時間間隔超過了session的超時設置;
c.服務器進程被停止(非持久session)
3、如何做到在瀏覽器關閉時刪除session
嚴格的講,做不到這一點。可以做一點努力的辦法是在所有的客戶端頁面裏使用javascript代碼window.oncolose()來監視瀏覽器的關閉動作,然後向服務器發送一個請求來刪除session。
但是對於瀏覽器崩潰或者強行殺死進程這些非常規手段仍然無能爲力。
4、有個HttpSessionListener是怎麼回事
你可以創建這樣的listener去監控session的創建和銷燬事件,使得在發生這樣的事件時你可以做一些相應的工作。注意是session的創建和銷燬動作觸發listener,而不是相反。類似的與HttpSession有關的listener還有HttpSessionBindingListener,HttpSessionActivationListener和 HttpSessionAttributeListener。
5、存放在session中的對象必須是可序列化的嗎
不是必需的。要求對象可序列化只是爲了session能夠在集羣中被複制或者能夠持久保存或者在必要時server能夠暫時把session交換出內存。
6、如何才能正確的應付客戶端禁止cookie的可能性
對所有的URL使用URL重寫,包括超鏈接,form的action,和重定向的URL,具體做法參見[6]http://e-docs.bea.com/wls/docs70/webapp/sessions.html#100770
7、開兩個瀏覽器窗口訪問應用程序會使用同一個session還是不同的session
對session來說是隻認id不認人,因此不同的瀏覽器,不同的窗口打開方式以及不同的cookie存儲方式都會對這個問題的答案有影響。
8、如何防止用戶打開兩個瀏覽器窗口操作導致的session混亂
這個問題與防止表單多次提交是類似的,可以通過設置客戶端的令牌來解決。就是在服務器每次生成一個不同的id返回給客戶端,同時保存在session裏,客戶端提交表單時必須把這個id也返回服務器,程序首先比較返回的id與保存在session裏的值是否一致,如果不一致則說明本次操作已經被提交過了。可以參看《J2EE核心模式》關於表示層模式的部分。需要注意的是對於使用javascript window.open打開的窗口,一般不設置這個id,或者使用單獨的id,以防主窗口無法操作,建議不要再window.open打開的窗口裏做修改操作,這樣就可以不用設置。
cookie問題
1、java中cookie設置
文章:https://blog.csdn.net/zyj66666/article/details/74784528
2、cookie如何創建
Web 服務器通過發送一個稱爲 Set-Cookie 的 HTTP 消息頭來創建一個 cookie,Set-Cookie消息頭是一個字符串,其格式如下(中括號中的部分是可選的):
Set-Cookie: value[; expires=date][; domain=domain][; path=path][; secure]
value部分,通常是一個 name=value 格式的字符串。事實上,這種格式是原始規範中指定的格式,但是瀏覽器並不會對 cookie 值按照此格式來驗證。實際上,你可以指定一個不含等號的字符串,它同樣會被存儲。然而,最常用的使用方式是按照 name=value 格式來指定 cookie 的值(大多數接口只支持該格式)。
發送回服務器的cookie只包含cookie設置的值,而不包含cookie的其他可選項,而且瀏覽器不會對cookie做任何更改,會原封不動的發送回服務器。當存在多個cookie時,用分號和空格隔開:
Cookie: name=value; name1=value1; name2=value2/pre>
參考博文:
cookie session的本質
深入分析理解session與cookie的作用
Session原理解釋,爲什麼使用