J2EE社區

          茍有恒,何必三更起五更眠;
          最無益,只怕一日曝十日寒.
          posts - 241, comments - 318, trackbacks - 0, articles - 16

          X-Java:http,session,cookie詳解

          Posted on 2010-12-15 14:44 xcp 閱讀(1543) 評論(0)  編輯  收藏 所屬分類: JAVA

          http,session,cookie一定是困擾了大家許久了的三個詞吧。

          下面跟我來。

          HTTP Session
          一、淺析
          HTTP協議(http://www.w3.org/Protocols/)是“一次性單向”協議。

          服務端不能主動連接客戶端,只能被動等待并答復客戶端請求。客戶端連接服務端,發出一個HTTP Request,服務端處理請求,并且返回一個HTTP Response給客戶端,本次HTTP Request-Response Cycle結束。

          我們看到,HTTP協議本身并不能支持服務端保存客戶端的狀態信息。于是,Web Server中引入了session的概念,用來保存客戶端的狀態信息。

          這里用一個形象的比喻來解釋session的工作方式。假設Web Server是一個商場的存包處,HTTP Request是一個顧客,第一次來到存包處,管理員把顧客的物品存放在某一個柜子里面(這個柜子就相當于Session),然后把一個號碼牌交給這個顧客,作為取包憑證(這個號碼牌就是Session ID)。顧客(HTTP Request)下一次來的時候,就要把號碼牌(Session ID)交給存包處(Web Server)的管理員。管理員根據號碼牌(Session ID)找到相應的柜子(Session),根據顧客(HTTP Request)的請求,Web Server可以取出、更換、添加柜子(Session)中的物品,Web Server也可以讓顧客(HTTP Request)的號碼牌和號碼牌對應的柜子(Session)失效。顧客(HTTP Request)的忘性很大,管理員在顧客回去的時候(HTTP Response)都要重新提醒顧客記住自己的號碼牌(Session ID)。這樣,顧客(HTTP Request)下次來的時候,就又帶著號碼牌回來了。

          Session ID實際上是在客戶端和服務端之間通過HTTP Request和HTTP Response傳來傳去的。號碼牌(Session ID)必須包含在HTTP Request里面。關于HTTP Request的具體格式,請參見HTTP協議(http://www.w3.org/Protocols/)。這里只做一個簡單的介紹。

          在Java Web Server(即Servlet/JSP Server)中,Session ID用jsessionid表示(請參見Servlet規范)。

          HTTP Request一般由3部分組成:

          (1)Request Line

          這一行由HTTP Method(如GET或POST)、URL、和HTTP版本號組成。

          例如,GET http://www.w3.org/pub/WWW/TheProject.html HTTP/1.1

          GET http://www.google.com/search?q=Tomcat HTTP/1.1

          POST http://www.google.com/search HTTP/1.1

          GET http://www.somsite.com/menu.do;jsessionid=1001 HTTP/1.1

           

          (2)Request Headers

          這部分定義了一些重要的頭部信息,如,瀏覽器的種類,語言,類型。Request Headers中還可以包括Cookie的定義。例如:

          User-Agent: Mozilla/4.0 (compatible; MSIE 5.5; Windows NT 5.0)

          Accept-Language: en-us

          Cookie: jsessionid=1001

           

          (3)Message Body

          如果HTTP Method是GET,那么Message Body為空。

          如果HTTP Method是POST,說明這個HTTP Request是submit一個HTML Form的結果,

          那么Message Body為HTML Form里面定義的Input屬性。例如,

          user=guest

          password=guest

          jsessionid=1001

          主意,如果把HTML Form元素的Method屬性改為GET。那么,Message Body為空,所有的Input屬性都會加在URL的后面。你在瀏覽器的URL地址欄中會看到這些屬性,類似于

          http://www.somesite/login.do?user=guest&password=guest&jsessionid=1001

           

          從理論上來說,這3個部分(Request URL,Cookie Header, Message Body)都可以用來存放Session ID。由于Message Body方法必須需要一個包含Session ID的HTML Form,所以這種方法不通用。

          一般用來實現Session的方法有兩種:

          (1)URL重寫。

          Web Server在返回Response的時候,檢查頁面中所有的URL,包括所有的連接,和HTML Form的Action屬性,在這些URL后面加上“;jsessionid=XXX”。

          下一次,用戶訪問這個頁面中的URL。jsessionid就會傳回到Web Server。

          (2)Cookie。

          如果客戶端支持Cookie,Web Server在返回Response的時候,在Response的Header部分,加入一個“set-cookie: jsessionid=XXXX”header屬性,把jsessionid放在Cookie里傳到客戶端。

          客戶端會把Cookie存放在本地文件里,下一次訪問Web Server的時候,再把Cookie的信息放到HTTP Request的“Cookie”header屬性里面,這樣jsessionid就隨著HTTP Request返回給Web Server。

          二、相關資料
          前面是我自作聰明的一段個人淺見,下面我來找點權威資料支持。

          http://www.w3.org/Protocols/

           

          Use of HTTP State Management (RFC 2964).

          ftp://ftp.rfc-editor.org/in-notes/rfc2964.txt

           

          這個文件是定義“HTTP State Management”擴展協議部分。里面有這么一段,

          It's important to realize that similar capabilities may also be    achieved using the "bare" HTTP protocol, and/or dynamically-generated

          HTML, without the State Management extensions.  For example, state information can be transmitted from the service to the user by    embedding a session identifier in one or more URLs which appear in HTTP redirects, or dynamically generated HTML; and the state information may be returned from the user to the service when such URLs appear in a GET or POST request.  HTML forms can also be used to pass state information from the service to the user and back, without the user being aware of this happening.

           
          這段話的意思是說,不用這個 “HTTP State Management”擴展協議部分,我們也可以用“純粹”的HTTP協議實現Session -- 比如URL重寫,HTML Form等。

          這里面沒有提到Cookie。因為“HTTP State Management” 擴展協議部分本身包括了關于Cookie的內容。這一點可以通過

          HTTP State Management Mechanism (RFC 2965),

          ftp://ftp.rfc-editor.org/in-notes/rfc2965.txt

          看出來。

           

          STATE AND SESSIONS

          This document describes a way to create stateful sessions with HTTP    requests and responses.  Currently, HTTP servers respond to each    client request without relating that request to previous or    subsequent requests; the state management mechanism allows clients    and servers that wish to exchange state information to place HTTP    requests and responses within a larger context, which we term a    "session".  This context might be used to create, for example, a    "shopping cart", in which user selections can be aggregated before    purchase, or a magazine browsing system, in which a user's previous    reading affects which offerings are presented.

          Neither clients nor servers are required to support cookies.  A    server MAY refuse to provide content to a client that does not return    the cookies it sends.

           

          后面還給出了例子(其中的漢語部分是我加的)。

           

          4.1  Example 1

          Most detail of request and response headers has been omitted.  Assume

          the user agent has no stored cookies.

           

                1. User Agent -> Server

           

                  POST /acme/login HTTP/1.1

                  [form data]

           

                  User identifies self via a form.

           

                2. Server -> User Agent

           

                  HTTP/1.1 200 OK

                  Set-Cookie2: Customer="WILE_E_COYOTE"; Version="1"; Path="/acme"

           

                  Cookie reflects user's identity.

          (這里面的 Customer="WILE_E_COYOTE" 應該就是從Form Data里面來的,這時候又傳回給了客戶端)

           

                3. User Agent -> Server

           

                  POST /acme/pickitem HTTP/1.1

                  Cookie: $Version="1"; Customer="WILE_E_COYOTE"; $Path="/acme"

                  [form data]

           

                  User selects an item for "shopping basket".

           

                4. Server -> User Agent

           

                  HTTP/1.1 200 OK

                  Set-Cookie2: Part_Number="Rocket_Launcher_0001"; Version="1";

                          Path="/acme"

           

                  Shopping basket contains an item.

          (這個火箭發射器顯然也是從Form Data來的。但為什么Part_Number="Rocket_Launcher_0001"也需要傳回給客戶端?

          Customer="WILE_E_COYOTE"; 應該是在傳統的”Set-Cookie”里面傳回給客戶端的。” Set-Cookie2” 的作用應該是向Cookie里面添加東西。)

           

                5. User Agent -> Server

           

                  POST /acme/shipping HTTP/1.1

                  Cookie: $Version="1";

                          Customer="WILE_E_COYOTE"; $Path="/acme";

                          Part_Number="Rocket_Launcher_0001"; $Path="/acme"

                  [form data]

           

                  User selects shipping method from form.

           

          (客戶傳給服務器的Cookie里面包括了Customer和Part_Number)

           

                6. Server -> User Agent

           

                  HTTP/1.1 200 OK

                  Set-Cookie2: Shipping="FedEx"; Version="1"; Path="/acme"

           

                  New cookie reflects shipping method.

           

                7. User Agent -> Server

           

                  POST /acme/process HTTP/1.1

                  Cookie: $Version="1";

                          Customer="WILE_E_COYOTE"; $Path="/acme";

                          Part_Number="Rocket_Launcher_0001"; $Path="/acme";

                          Shipping="FedEx"; $Path="/acme"

                  [form data]

           

           

                  User chooses to process order.

           

                8. Server -> User Agent

           

                  HTTP/1.1 200 OK

           

                  Transaction is complete.

           

             The user agent makes a series of requests on the origin server, after each of which it receives a new cookie.  All the cookies have the same Path attribute and (default) domain.  Because the request-URIs all path-match /acme, the Path attribute of each cookie, each request contains all the cookies received so far.

          (看到這里,我才大致明白,原來那個$Path="/acme" 大致起著 JSessionID的作用)

           

          三、Tomcat5的HTTP Session實現
          下面我們來看Tomcat5的源代碼如何支持HTTP 1.1 Session。

          我們可以用jsessionid, Set-Cookie等關鍵字搜索Tomcat5源代碼。

           

          首先,我們來看常量定義:

          org.apache.catalina.Globals

           

              /**

               * The name of the cookie used to pass the session identifier back

               * and forth with the client.

               */

              public static final String SESSION_COOKIE_NAME = "JSESSIONID";

           

           

              /**

               * The name of the path parameter used to pass the session identifier

               * back and forth with the client.

               */

          public static final String SESSION_PARAMETER_NAME = "jsessionid";

           

          Cookie里面用大寫的JSESSIONID,URL后綴用的是小寫的jsessionid。

          Session 的具體實現類是org.apache.catalina.session.StandardSession。一個Tomcat Server的所有Session都由一個Manager(擁有一個Context)統一管理。我估計有一個 Session Listener 專門管理Cluster之間的Session數據復制,具體的我沒有追查下去。

           

          另外幾個重要的相關類是org.apache.coyote.tomcat5包下面的CoyoteRequest , CoyoteResponse, CoyoteAdapter三個類。

           

          org.apache.coyote.tomcat5.CoyoteResponse類的toEncoded()方法支持URL重寫。

          String toEncoded(String url, String sessionId) {



                  StringBuffer sb = new StringBuffer(path);

                  if( sb.length() > 0 ) { // jsessionid can't be first.

                      sb.append(";jsessionid=");

                      sb.append(sessionId);

                  }

                  sb.append(anchor);

                  sb.append(query);

                  return (sb.toString());

          }

           

          我們來看org.apache.coyote.tomcat5.CoyoteRequest的兩個方法configureSessionCookie()

          doGetSession()用Cookie支持jsessionid.

           

              /**

               * Configures the given JSESSIONID cookie.

               *

               * @param cookie The JSESSIONID cookie to be configured

               */

              protected void configureSessionCookie(Cookie cookie) {

                 …

              }

           

              HttpSession doGetSession(boolean create){

                …

                  // Creating a new session cookie based on that session

                  if ((session != null) && (getContext() != null)

                         && getContext().getCookies()) {

                      Cookie cookie = new Cookie(Globals.SESSION_COOKIE_NAME,

                                                 session.getId());

                      configureSessionCookie(cookie);

                      ((HttpServletResponse) response).addCookie(cookie);

                  }

                …

              }

           

          四、More
          HTTP Session的協議、規范、實現大概就是這些。

          另外,在frameset中使用Session的情況有些復雜,不同環境表現可能不同。

          其余相關的概念有“單點登錄”(Sing Sign On – SSO), Domain, Cluster, Proxy, Cache等。




          名稱: ?4C.ESL | .↗Evon
          口號: 遇到新問題?先要尋找一個方案乄而不是創造一個方案こ
          mail: 聯系我


          主站蜘蛛池模板: 元阳县| 义马市| 万安县| 甘孜县| 玉溪市| 顺昌县| 札达县| 三江| 镇赉县| 繁昌县| 赣州市| 工布江达县| 谷城县| 黄石市| 建宁县| 宁波市| 横峰县| 密山市| 嘉荫县| 温泉县| 平武县| 罗田县| 金坛市| 吉木乃县| 隆尧县| 安阳市| 正定县| 剑川县| 罗山县| 墨玉县| 嘉善县| 亚东县| 怀柔区| 楚雄市| 东乡族自治县| 闽侯县| 阳谷县| 四会市| 双流县| 贵定县| 淳化县|