天天看點

從一開始迷糊到現在的session和cookie

如果你說你不會session和cookie,學java的人會對你嗤之以鼻,因為那是基礎的技術,是面試中簡單的問題,在網上有大量資料。但是,這并不是一個簡單的問題。因為session這個詞在被濫用,我讀很多文檔時都碰到過session,有時候人們說它是一次會話(會話又是什麼?),有時說它是jsp的一個預設作用域,有時又說它是浏覽器打開到關閉的一個情況……

關鍵的問題是,當我們在談session與cookie時,它指的是什麼?

session與cookie刨根問底

      • RFC2109
      • Cookie
      • JSESSIONID

RFC2109

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 technique 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.

文檔說的很清楚,為了解決http協定的無狀态性,我們需要一種技術,這種技術能把http request和http response置于一種更大的上下文中,我們把它叫做

session

There are, of course, many different potential contexts and thus many different potential types of session. The designers’ paradigm for sessions created by the exchange of cookies has these key attributes:
  1. Each session has a beginning and an end.
  2. Each session is relatively short-lived.
  3. Either the user agent or the origin server may terminate a session.
  4. The session is implicit in the exchange of state information.

由于上下文有很多種,是以

session

也有很多種。由cookie來實作的

session

有幾個特點:

  • 每個session有開始有結束
  • 每個session相對短命
  • 用戶端和服務端都能終結一個session
  • session在資訊交換中是隐式存在的
The origin server initiates a session, if it so desires. (Note that “session” here does not refer to a persistent network connection but to a logical session created from HTTP requests and responses. The presence or absence of a persistent connection should have no effect on the use of cookie-derived sessions). To initiate a session, the origin server returns an extra response header to the client, Set-Cookie.

它說服務端初始化一個

session

,然後我們一直講的

session

是一個邏輯概念,而大家平時講的session是一種持續的用戶端和服務端的網絡連接配接。不管有沒有這種連接配接,都不會影響cookie-derived的

session

的使用。

A user agent returns a Cookie request header (see below) to the origin server if it chooses to continue a session. The origin server may ignore it or use it to determine the current state of the session. It may send back to the client a Set-Cookie response heade with the same or different information, or it may send no Set-Cookie header at all. The origin server effectively ends a session by sending the client a Set-Cookie header with Max-Age=0.

origin server把cookie放在response header中,user agent回的cookie放在request header中。

文法和解析的細節我就不說了,大家可以自己去查閱文檔。

它這裡舉了一個很好的例子:

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-Cookie: Customer="WILE_E_COYOTE"; Version="1"; Path="/acme"

         Cookie reflects user's identity.
           

服務端把cookie放在header中。

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."
           

使用者選了一件商品。user agent回的時候在

Version

Path

前都加了

$

,因為origin server在解析的時候會從左往右讀,讀到不是

$

開頭的資訊就找到了該cookie的

name

(這裡是

Customer

)。

4.  Server -> User Agent

         HTTP/1.1 200 OK
         Set-Cookie: Part_Number="Rocket_Launcher_0001"; Version="1";
                 Path="/acme"

         Shopping basket contains an item.

           

購物車裡放了商品。

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.
           

使用者選擇支付手段。

6.  Server -> User Agent

         HTTP/1.1 200 OK
         Set-Cookie: Shipping="FedEx"; Version="1"; Path="/acme"

         New cookie reflects shipping method.
           

header中新添加的cookie反映了使用的支付手段。

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.
           

成功,交易結束。

這個例子完美展示了如何通過cookie實作服務端和用戶端的交流。

Cookie

上面的是規範,tomcat怎麼實作的呢?

javax.servlet.http.Cookie

的文檔:

/**
 * Creates a cookie, a small amount of information sent by a servlet to a Web
 * browser, saved by the browser, and later sent back to the server. A cookie's
 * value can uniquely identify a client, so cookies are commonly used for
 * session management.
 * <p>
 * A cookie has a name, a single value, and optional attributes such as a
 * comment, path and domain qualifiers, a maximum age, and a version number.
 * Some Web browsers have bugs in how they handle the optional attributes, so
 * use them sparingly to improve the interoperability of your servlets.
 * <p>
 * The servlet sends cookies to the browser by using the
 * {@link HttpServletResponse#addCookie} method, which adds fields to HTTP
 * response headers to send cookies to the browser, one at a time. The browser
 * is expected to support 20 cookies for each Web server, 300 cookies total, and
 * may limit cookie size to 4 KB each.
 * <p>
 * The browser returns cookies to the servlet by adding fields to HTTP request
 * headers. Cookies can be retrieved from a request by using the
 * {@link HttpServletRequest#getCookies} method. Several cookies might have the
 * same name but different path attributes.
 * <p>
 * Cookies affect the caching of the Web pages that use them. HTTP 1.0 does not
 * cache pages that use cookies created with this class. This class does not
 * support the cache control defined with HTTP 1.1.
 * <p>
 * This class supports both the RFC 2109 and the RFC 6265 specifications.
 * By default, cookies are created using RFC 6265.
 */
           

因為cookie能夠用來定位唯一确定的client,是以被用作

session

管理。

cookie既然能實作用戶端和服務端的交流,那為什麼要出現其他

session

技術呢?

因為cookie有缺點。

由于cookie暴露在了浏覽器,是以不安全,而且容量小。

JSESSIONID

是以我們要用到

HttpSession

public interface HttpSession

Provides a way to identify a user across more than one page request or visit to a Web site and to store information about that user.

The servlet container uses this interface to create a session between an HTTP client and an HTTP server. The session persists for a specified time period, across more than one connection or page request from the user. A session usually corresponds to one user, who may visit a site many times. The server can maintain a session in many ways such as using cookies or rewriting URLs.

This interface allows servlets to

1.View and manipulate information about a session, such as the session identifier, creation time, and last accessed time

2.Bind objects to sessions, allowing user information to persist across multiple user connections

HttpSession

與使用者一一對應。是以當一個使用者登入JD之後,就不用在購物頁面、支付頁面重複登入了。

HttpSession

在這方面與

Cookie

一模一樣,它們都是

session

的具體實作。

至于關掉浏覽器後,cookie是否還在,session是否還在,這個都可以自己寫代碼試一下。

現在的問題是,

HttpSession

怎麼與一個client一一對應呢?

我們在

HttpSession session = request.getSession();

上打一個斷點,觀察浏覽器第一次通路時的情況。

從一開始迷糊到現在的session和cookie

一直get。像getBean一樣。

從一開始迷糊到現在的session和cookie

create。

這裡的

manager

也是tomcat中的頂級管理接口。

從一開始迷糊到現在的session和cookie
從一開始迷糊到現在的session和cookie

建立session對象。

注意這裡:

從一開始迷糊到現在的session和cookie

generateSessionId

産生了sessionID,就是那一長串的32個字元的字元串。

服務端建立的session對象:

從一開始迷糊到現在的session和cookie

這個

StandardSession

就是

HttpSessin

的一個實作類。

服務端自己建立session之後,還要做一個工作:

從一開始迷糊到現在的session和cookie

他要在這個session的基礎之上建立一個cookie。

從一開始迷糊到現在的session和cookie

這個cookie的value就是

sessionID

的值。

那麼這個特殊的cookie的key是多少呢?

從一開始迷糊到現在的session和cookie

那就是神秘的

JSESSIONID

也就是說,當浏覽器第一次通路伺服器的時候,服務端會生成一個

sessionID

,然後以

JSESSIONID

作為key,以

sessionID

的值作為value建立一個cookie,然後添加到header中:

從一開始迷糊到現在的session和cookie

于是,浏覽器的

JSESSIONID

的value就和服務端的

sessionID

對應了。

我們可以說,

HttpSession

是建立在

Cookie

之上的。