如果你说你不会session和cookie,学java的人会对你嗤之以鼻,因为那是基础的技术,是面试中简单的问题,在网上有大量资料。但是,这并不是一个简单的问题。因为session这个词在被滥用,我读很多文档时都碰到过session,有时候人们说它是一次会话(会话又是什么?),有时说它是jsp的一个默认作用域,有时又说它是浏览器打开到关闭的一个情况……
关键的问题是,当我们在谈session与cookie时,它指的是什么?
session与cookie刨根问底
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:
- Each session has a beginning and an end.
- Each session is relatively short-lived.
- Either the user agent or the origin server may terminate a session.
- 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();
上打一个断点,观察浏览器第一次访问时的情况。
一直get。像getBean一样。
create。
这里的manager
也是tomcat中的顶级管理接口。
创建session对象。
注意这里:
generateSessionId
产生了sessionID,就是那一长串的32个字符的字符串。
服务端创建的session对象:
这个StandardSession
就是HttpSessin
的一个实现类。
服务端自己创建session之后,还要做一个工作:
他要在这个session的基础之上创建一个cookie。
这个cookie的value就是sessionID
的值。
那么这个特殊的cookie的key是多少呢?
那就是神秘的JSESSIONID
。
也就是说,当浏览器第一次访问服务器的时候,服务端会生成一个sessionID
,然后以JSESSIONID
作为key,以sessionID
的值作为value创建一个cookie,然后添加到header中:
于是,浏览器的JSESSIONID
的value就和服务端的sessionID
对应了。
我们可以说,HttpSession
是建立在Cookie
之上的。