不同瀏覽器Session使用和常見問題分析
session跟蹤機制
HTTP協議為無狀態協議,對於每一個web請求,伺服器是無法知道是否為同一個請求者。那麼是怎麼樣跟蹤請求的呢?
目前有兩種方式。
第一種,使用cookie記錄標誌。瀏覽器是可以儲存cookie資訊的,在請求響應的時候,建立一個對應的cookie,記錄一個編號,下次再發送請求的時候,將cookie記錄的資訊傳送到伺服器上,伺服器取得這個值,就可以判斷是否為同一個使用者傳送的請求了。
第二種,使用URL重寫。每次URL後面跟隨一個引數,伺服器取得這個引數,也可以判斷是否為同一個使用者的請求。
index.html; jsessionid=1234
對應用伺服器來說,會先檢查瀏覽器是否支援cookie,如果支援就採用cookie,如果不支援則自動採用URL重寫,這個過程是由Servlet規範規定的,所有的Servlet容器都需要實現這樣的功能,不需要人為干預。
session超時機制
當然,如果伺服器一直儲存session資訊,使用者關閉了瀏覽器,或者離開了網站,session資訊就沒有必要儲存在伺服器上了。怎麼清理呢?
Session的超時機制就是來處理這種情況的,如果在規定的時間內,第一次請求時,伺服器建立新的session,並且會記錄時間,如果很長時間沒有資訊相同的session id資訊過來,伺服器會人為使用者沒有使用了,就自動清理掉這個對應的這個session了。
參考web.xml的配置sessiontimeout項。
JSP作用域
1、page
這個作用域是最嚴格的,此物件只對於它所在JSP頁面是可訪問的。
2、request
這個作用域的物件是在相應請求的生命週期內有效。也就是說,物件在建立它的頁面內有效,以及請求被轉發(forward方法)或者被包含(include方法)的頁面內有效。
3、session
會話域的物件,對於參與某個客戶端會話週期的所有請求,資料都是一致的。如果有多個請求操作session,可能會有多執行緒的問題。
比如先發送A請求,立即傳送B請求:
A請求訪問session,設定某一個屬性的值為a。
B請求讀取session,讀取A設定的屬性值。
如果在A響應之前B請求已經發出去了,B讀取的時候A還沒有設定,B讀取到的值就不是a了。
4、application
這是最寬泛的作用域,application裡的變數可以被所有使用者共享,類似全域性變數。這類物件不是執行緒安全的,如果多個請求試圖同時修改某一個物件,那麼必須對這些訪問進行同步處理。
如果使用者甲的操作修改了application中的變數,使用者乙訪問時得到的是修改後的值。這在其他 scope中都是不會發生的,page, request, session都是完全隔離的,無論如何修改都不會影響其他人的資料。
HttpSession介面
Servlet API只是定義了一組介面和一些實現類,HttpSession就是一個介面,具體的實現類不同的servlet容器有不同的實現。
比如weblogic8裡面使用的是HashTable來儲存session的屬性和值,而tomcat6裡面則是使用ConcurrentHashMap來儲存。
HttpSession常用方法:
getAttribute(java.lang.String name) |
返回session中繫結的變數名的值,如果沒有則返回null |
getId() |
返回jsessionId的值 |
invalidate() |
取消當前會話,系統退出之前呼叫 |
removeAttribute(java.lang.String name) |
刪掉session某繫結的變數 |
setAttribute(java.lang.String name, java.lang.Object value) |
新增對於的名稱和值到session中 |
什麼情況下使用session
1. 儲存使用者特定資料,比如使用者角色,操作集合等
2. 多個不同請求之間的共享資料
什麼情況下不能使用session
1. 對所有使用者都可以共享的資料,比如機構列表快取,系統某功能的配置基表等
2. 資料提交前的臨時存放,可能存在資料覆蓋的情況,使用request轉發或使用隱藏表單
針對同一域中的操作的比較
新視窗開啟方式 | IE6 | IE8 |
CTRL+N | 共享session | 共享session |
winodw.open | 共享session | 共享session |
連結 | 共享session | 共享session |
開啟IE | 新建session | 共享session |
建立標籤頁 | 無 | 共享session |
對於同一使用者從同一系統中同時開啟多個相同的頁面,此時瀏覽器會共享session,如果這個頁面操作,伺服器上有更新session物件的資訊,可能會導致session資訊會被覆蓋。
舉例:
開啟頁面page.jsp後,輸入資訊,然後提交,提交時從session取key的值。
假設某頁面page.jsp每次開啟之後,都會根據資料設定session。
第一次的資料值為value1:
session.setAttribute("key","value1");
第二次的資料值為value2:
session.setAttribute("key","value2");
同時開啟兩個page.jsp,第一次開啟之後未提交,第二次開啟之後也未提交。
此時,如果回到第一個頁面,並提交,取到的key值是value2,資料有異常。
回到第二個頁面,並提交,取到的key值是value2,資料正常。
規避措施:
a. 禁止同一頁面同時開啟多次
b. a條件不滿足的情況下,不使用session儲存資料,考慮使用頁面表單的hidden域傳值,或者使用request請求轉發
c. 對於GET請求,可以使用URL重寫,加入引數,敏感資訊需要加密不能使用
如果需要儲存臨時資料,該怎麼辦
有時候為了增加客戶體驗,需要在後面的頁面中保留上一個頁面輸入的內容,例如分頁顯示列表,這個時候有兩種做法可以選擇。
A. 使用頁面表單隱藏資料。將計算得到總條數,總頁數,每頁的數量,當前頁數,開始的索引等資料放到頁面表單中,當選擇下一頁或者上一頁時,每次都將這些資料傳到後臺進行處理。
B. 使用session快取。將計算得到總條數,總頁數,每頁的數量,當前頁數,開始的索引等資料組裝成分頁物件放到session中,當選擇下一頁或者上一頁時,後臺從session中獲取資料,再進行處理。
對於同一客戶不同產品進行的受理,存在session資料覆蓋的情況,此時就要特別注意了。如果改造成頁面隱藏欄位的方式確實很困難,在使用session的時候,可以對不同的產品進行拆分,使用跟產品相關的session key值,比如,可以使用保單號作為session的key,而不是使用保單物件。
程式碼示例:
PolicyDTO policy=new PolicyDTO ();
session.setAttribute(“policyDTO”,policy);//會產生資料覆蓋問題
session.setAttribute(policyDTO.getPolicyNo,policy);//不會產生資料覆蓋問題
在服務退出或者受理提交之後,需要清理掉session中的資料,否則會造成垃圾資料過多,伺服器記憶體洩露。
在IE6中,不同方式會有不同的行為,在IE8中,不同視窗共享一個會話。如果要使IE8和IE6在開啟IE時,表現一致,在IE8啟動引數中加入-nomerge。
對於多個不同請求之間共享的資料,如果放在session中,可能存在併發的問題。
請求響應的時間,由於網路,伺服器等因素可能慢,也可能快,如果先後傳送兩個請求到伺服器,可能後傳送的請求會先響應,這時取到的值就不是期望的了。
示意圖如下,和圖三是有區別的,2A表示先於寫之前取值,2B表示正常的情況。
對於這樣的情況,可以使用順序操作,設定session之後,跳轉到一個新的頁面,在新的頁面上在去讀session裡面的資料。見示意圖五。
模態視窗session丟失問題
在IE中使用showModalDialog方法開啟一個新的視窗的時候,會建立一個新的視窗例項。如果這個例項是在新的程序中開啟的,所有的cookies和session id都不能使用了,這個例項和winodw.open方法開啟的例項是不一樣的,也就是說showModalDialog在新程序中開啟的視窗,和父視窗不共享session,這裡使用的時候需要特別注意。
關於showModalDialog方法的詳細描述,請參考連結文件:
在msdn上搜索 showModalDialog method
此處無法提供連結