原文地址 https://blog.csdn.net/collonn/article/details/5698906
居然有3W的訪問量,好,我就把session和cookie的關(guān)系先來個(gè)總結(jié),注意,是最最簡單直白明了的總結(jié)了。
http協(xié)議,協(xié)議,協(xié)議,重要的說3遍,http協(xié)議主要有2大塊,請求頭和請求體,cookie在http請求頭里,就是一個(gè)由多個(gè)KV組成的的字符串而已,其中有一個(gè)key=JSESSIONID。
client---the 1st time--->sever,header 的cookie中無JSSIONID
server--response--->client, server發(fā)現(xiàn)請求頭中無JSSIONID,好,我來給你產(chǎn)生一個(gè)隨機(jī)值,返回給你,在response的header中,多了一個(gè)東西, Set-Cookie:JSESSIONID=xxx
client--reveive response from server,發(fā)現(xiàn)response header中有Set-Cookie,好,以后我再請求server時(shí),把這些Cookie都帶上,其中就包含了JSESSIONID
client--the 2nd time--->server, 帶上了所有的cookie內(nèi)容
server---response--->client,帶上了所有的cookie內(nèi)容
所以,基于session的會話保持,是靠client和server共同協(xié)作才能完成的,server產(chǎn)生sessionId,然后大家一直傳來傳去,就這么一直玩下去,直到session timeout,或注銷。
今天說說:會話狀態(tài)保持,JSESSIONID,COOKIE之間的關(guān)系
在服務(wù)器端,我們用慣了session.setAttribute("",userInfo)這樣的一行代碼,估計(jì)你很少想到:服務(wù)器與瀏覽器之間是如何保持會話狀態(tài)的。好了,先引用一些文章的精彩片段:
http://www.xxx.com/xxx_app;jsessionid=xxxxxxxxxx?a=x&b=x。
這跟一般的url基本一樣,只有一個(gè)地方有區(qū)別,那就是“;jessionid=xxxxxxxx”。這個(gè)參數(shù)有時(shí)候有,有時(shí)候又沒有,說它是參數(shù)可又跟一般傳遞的參數(shù)不同,它是緊跟在url后面用分號來分隔的,用一般的request.getParameter()方法還取不到j(luò)sessionid。
啟動你的tomcat,打開FireFox(愛得不得了,一定要安裝FireBug),輸入localhost就行,打開firebug,點(diǎn)網(wǎng)絡(luò),你會看到,瀏覽器與服務(wù)器會話的信息,給出瀏覽器
(1)第一次請求服務(wù)器:
瀏覽器的請求頭信息
Host | localhost |
User-Agent | Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN; rv:1.9.2.6) Gecko/20100625 Firefox/3.6.6 |
Accept | text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 |
Accept-Language | zh-cn,zh;q=0.5 |
Accept-Encoding | gzip,deflate |
Accept-Charset | GB2312,utf-8;q=0.7,*;q=0.7 |
Keep-Alive | 115 |
Connection |
|
在下圖
在下圖
服務(wù)器響應(yīng)頭信息
Server | Apache-Coyote/1.1 |
Set-Cookie | JSESSIONID=64D21B4D69DFB3041B6375C1932BD6CB; Path=/ |
Content-Type | text/html;charset=UTF-8 |
Content-Language | zh-CN |
Content-Length | 242 |
Date | Mon, 28 Jun 2010 02:35:29 GMT |
(2)第二次請求服務(wù)器:
瀏覽器的請求頭信息
Host | localhost |
User-Agent | Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN; rv:1.9.2.6) Gecko/20100625 Firefox/3.6.6 |
Accept | text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 |
Accept-Language | zh-cn,zh;q=0.5 |
Accept-Encoding | gzip,deflate |
Accept-Charset | GB2312,utf-8;q=0.7,*;q=0.7 |
Keep-Alive | 115 |
Connection | keep-alive |
Cookie | JSESSIONID=64D21B4D69DFB3041B6375C1932BD6CB |
服務(wù)器響應(yīng)頭信息
Server | Apache-Coyote/1.1 |
Content-Type | text/html;charset=UTF-8 |
Content-Language | zh-CN |
Content-Length | 242 |
Date | Mon, 28 Jun 2010 02:37:51 GMT |
重復(fù)第三次,每四次...第N次請求服務(wù)器,瀏覽器和服務(wù)器的請求頭信息都是與第二次請求服務(wù)器是一樣的。
(3)但是,如果你在服務(wù)器端加入如下一行代碼:
Log.info("SessionId:" + request.getSession().getId());
你會看到,當(dāng)你第一次請求服務(wù)器時(shí),就會默認(rèn)有一個(gè)新的session被創(chuàng)建,而且在session的有效時(shí)間范圍內(nèi),這個(gè)輸出值是不會變的,否則,服務(wù)器會重新創(chuàng)建一個(gè)session,自然,sessionId也就不同了,這段代碼的輸出自然也會不同了。
(4)你必須注意這一點(diǎn):你用的是瀏覽器與服務(wù)器通信:
有一些事情是瀏覽器幫助我們?nèi)プ隽耍蔷褪牵寒?dāng)你第一次與服務(wù)器通信時(shí),瀏覽器會保存服務(wù)器返回的Set-Cookie這個(gè)健的值(JSESSIONID=64D21B4D69DFB3041B6375C1932BD6CB
),只要你不關(guān)閉瀏覽器(徹底關(guān)閉,關(guān)閉選項(xiàng)卡不算),瀏覽器會從第二次向服務(wù)器發(fā)出請求開始,一直帶上這個(gè)鍵值對,發(fā)給服務(wù)器。服務(wù)器就會知道,這是同一個(gè)人(同一個(gè)會話)發(fā)起的請求。
(5)我們再注意一下:request.setAttribute("sysuser",userInfo)這句話:
當(dāng)你第一次請求服務(wù)器時(shí),這句代碼會根據(jù)服務(wù)器默認(rèn)產(chǎn)生的session得到ID,并與sysuser=userInfo這個(gè)鍵值對掛上鉤(當(dāng)然,userInfo可以是任何對象),保證唯一關(guān)聯(lián),檢測用戶是否登錄就是這樣實(shí)現(xiàn)的。
我一定要聲明一點(diǎn):保持一個(gè)會話與用戶是否登錄是沒有任何關(guān)系的。
(6)再次引深一下,如果你用的不是瀏覽器,比如說做J2ME開發(fā),怎樣保持會話呢?
(1)在你寫完這行代碼后:HttpConnection hc = (HttpConnection)Connector.open(httpURL),加入以下代碼:(Constant.sessionID只是一個(gè)靜態(tài)變量)
//在與服務(wù)器通信前設(shè)置sessionId,維持唯一的一個(gè)會話if (Constant.sessionID != null) { hc.setRequestProperty("Cookie", AppContext.CurrentAppContext.sessionID);}
(2)A:只向服務(wù)器讀數(shù)據(jù),不向服務(wù)寫數(shù)據(jù),B:先向服務(wù)器寫數(shù)據(jù),再從服務(wù)器讀數(shù)據(jù)
對于這兩種情況,只要你第一次打開openDataInputStream(),這可以加入以下代碼(Constant.isLogin只是一個(gè)靜態(tài)變量boolean):
//每次與服務(wù)器通信后,保存 sessionIdString cookie = hc.getHeaderField("Set-Cookie");if (cookie != null) { String jsessionId = cookie.substring(0,cookie.indexOf(";")); if(Constant.sessionID != null && !Constant.sessionID.equals(jsessionId) && Constant.isLogin ){ Log.info("sessionid超時(shí), will get new sessionid, but you must login again"); //設(shè)置為未登錄狀態(tài) Constant.isLogin = false; } Constant.sessionID = jsessionId;}
這樣就可以保持一個(gè)會話了。
(7)最后,關(guān)于URL重定向
引用一段話:sun幫我們想到了,所以提供了2個(gè)方法來使事情變得簡單:response.encodeURL()和response.encodeRedirectURL()。這2個(gè)方法會判斷cookie是否可用,如果禁用了會解析出url中的jsessionid,并連接到指定的url后面,如果沒有找到j(luò)essionid會自動幫我們生成一個(gè)。至于為什么要有2個(gè)方法?這2個(gè)方法有什么不同?google了一下,說是這2個(gè)方法在判斷是否要包含jsessionid的邏輯上會稍有不同。在調(diào)用 HttpServletResponse.sendRedirect前,應(yīng)該先調(diào)用encodeRedirectURL()方法,否則可能會丟失 Sesssion信息。這2個(gè)方法的使用方法如:response.sendRedirect(response.encodeURL("/myapp /input.jsp"));。如果cookie沒有禁用,我們在瀏覽器地址欄中看到的地址是這樣的:/myapp/input.jsp,如果禁用了 cookie,我們會看到:/myapp /input.jsp;jsessionid=73E6B2470C91A433A6698C7681FD44F4。所以,我們在寫web應(yīng)用的時(shí)候,為了保險(xiǎn)起見,應(yīng)該在程序里的每一個(gè)跳轉(zhuǎn)url上都使用這2個(gè)方法,來保證session的可用性。