Session 与 Cookie 的作用都是为了保持访问用户与后端服务器的交互状态
1 理解cookie
cookie的作用通俗地说就是当一个用户通过HTTP访问一个服务器时,这个服务器会将一些key/value键值对返回给客户端浏览器,并给这些数据加上一些限制条件,在条件符合时这个用户下次访问
这个服务器时,数据又被完整地带会给服务器。
这个作用就像你去超市购物时,第一次会给你办张购物卡,在这个购物卡里存放了你的个人信息,下次你再来这个超市的时候,超市会识别你的购物卡,下次直接购物就好了。
由于是同一个客户端发出的请求,每次发出的请求都会带有第一次访问时服务端设置的信息,这样服务端就可以根据Cookie值来划分访问的用户了。
1.1 Cookie属性项
- NAME=VALUE : 键值对,可以设置要保存的Key/Value,注意这里的NAME不能和其它属性项的名字一样
- Expires: 过期时间,在设置的某个时间点后该Cookie就会失效,如expires=Wednesday,09-Nov-99 23:12:40 GMT
- Domain : 生成该Cookie的域名,如 domain="jd.com""
- Path : 该Cookie是在当前那个路径下生成的,如path=/wp-admin/
- Secure : 如果设置了这个属性,那么只会在SSH连接时才会回传该Cookie
- Comment: 注释项,用户说明该Cookie有何用途
- CommentURI: 服务器为此Cookie提供提供的URI注释
- Discard: 是否在会话结束后丢弃该Cookie项,默认为false
- Max-Age :最大失效时间,与Expires不同的是这里设置的是在多少秒后失效
- Port: 该Cookie在什么端口下可以回传服务器,如果有多个端口,则以逗号隔开,如Port="80,81,8080"
在实际应用中,在响应头中这样设置 :Set-Cookie:userName="junshan";Version="1";Domain=".wkq.pub";Max-Age=1000。
1.2 Cookie如何工作
当我们用如下方式创建Cookie时:
@RestController
@ApiIgnore
public class HelloController {
String getCookie(Cookie[] cookies, String key) {
if (cookies != null) {
for (Cookie cookie : cookies) {
if (cookie.getName().equals(key)) {
return cookie.getValue();
}
}
}
return null;
}
@GetMapping("/get")
public void doGet(HttpServletRequest req, HttpServletResponse res) {
Cookie[] cookies = req.getCookies();
String userName = getCookie(cookies, "userName");
String userAge = getCookie(cookies, "userAge");
if (userName == null) {
res.addCookie(new Cookie("userName", "马瑾妍"));
}
if (userAge == null) {
res.addCookie(new Cookie("userAge", "21"));
}
res.getHeaders("Set-Cookie");
}
}
Cookie是如何加到HTTP的Header中的呢?下图是Tomcat创建Set-Cookie响应头的时序图。

从图中可以看出,真正构件Cookie是在org.apache.catalina.connector.Response类中完成的,调用generateCookieString方法将Cookie对象构造成一个
字符串,构造的字符串的格式如userName="wkq";Version="1";Domain=".jd.com";Max-Age=1000。然后将这个字符串命名为Set-Cookie添加到MimeHeaders中。
注意:
- 所创建Cookie的NAME不能和Set-Cookie或者Set-Cookie2的属性值一样,如果一样会抛出IllegalArgumentException。
- 所创建Cookie的NAME和VALUE的值不能设置成非ASCII字符,如果要使用中文,可以通过URLEncoder将其编码,否则会抛出IllegalArgumentException异常。
- 当NAME和VALUE的值出现一些TOKEN字符(如""、","等)时,构建返回头会将该Cookie的Version自动设置为1。
- 当在该Cookie的属性项中出现Version1的属性项时,构建HTTP响应头同样会将Version设置为1。
当我们通过response.addCookie创建多个Cookie时,这些Cookie最终是以独立的Header项存在的,通俗的说就是每次创建Cookie时都是以创建一个以NAME为Set-Cookie的MimeHeaders
,每次调用addCookie时,最终都会创建一个Header
找到Tomcat最终构造Http响应头的代码,这段代码位于org.apache.coyote.http11.Http11Processor类中的prepareResponse方法中,如下所示
keepAliveTimeout=headers.size();
for(int i=0;i<keepAliveTimeout; ++i){
this.outputBuffer.sendHeader(headers.getName(i),headers.getValue(i));
}
在构建HTTP返回字节流时是将Header中的所有的项顺序地写出,而没有进行任何修改。所以可以想象浏览器在接受HTTP返回的数据时是分别解析每一个Header项的。
当我们请求某个URL路径时,浏览器会根据这个URL路径将符合条件的Cookie放在Request请求头中回传给服务端,服务端通过request.getCookies来取得所有的Cookie。
1.3 使用Cookie的限制
Cookie是HTTP头里的一个字段,虽然HTTP本身对这个字段并没有多少限制,但是Cookie最终还是存储在浏览器里,所以不同的浏览器对Cookie的存储都有一些限制,比如在Chrome浏览器中每个域名下的Cookie数量不能超过50个,总大小不能大于80000
2 理解Session
Cookie可以让服务器端程序跟踪每个客户端的访问,但是每次客户端的访问都必须传回这些Cookie,如果Cookie很多,则无形地增加了客户端与服务器端的数据传数量,而Session的出现正是为了解决这个问题。
同一个客户端每次和服务器交互时,不需要每次都传回所有的Cookie值,而是只要传回一个ID,这个ID是客户端第一次访问服务器时生成的,而且每个客户端是唯一的。这样每个客户端就有了一个唯一的ID,客户端只要传回这个ID就行了,这个ID通常是NAME为JSESSIONID的一个Cookie
2.1 Session与Cookie
下面详细讲解了Session是如何基于Cookie来工作的。实际上有以下三种方式可以放Session正常工作。
- 基于URL Path Parameter ,默认支持
- 基于Cookie,如果没有修改Context容器的Cookie标识,则默认也是支持的。
- 基于SSL,默认不支持,只有connector.getAttribute("SSLEnabled")为TRUE时才支持。
在第一种情况下,当浏览器不支持Cookie功能时,浏览器会将用户的SessionCookieName重写到用户请求的URL参数中,它的传递格式如
/path/Servlet;name=value;name2=value2?Name3=value3,其中 “Servlet;”后面的K-V就是要传递的Path Parameters,服务器会从这个Path
Parameters中拿到用户配置的SessionCookieName。关于这个SessionCookieName,就是大家熟知的"JSESSONID".
与Session关联的Cookie与其它Cookie并没什么不同。接着Request根据这个SessionCookieName到Parameters中拿到SessionID并设置到request.setRequestedSessionId中。
如果客户端支持Cookie,则Tomcat人会解析Cookie中的SessionId,并会覆盖URL中的SessionID
如果是第三种情况,则会根据javax,servlet.request.ssl_session属性值设置Session ID。
2.2 Session 如何工作
有了SessionID,服务端就可以创建HttpSession对象了,第一次触发通过request.getSession()
方法。如果当前的SessionID还没有对应的HttpSession对象,那么就创建一个新的,并将这个对象添加到
org.apache.catalina.Manager的Sessions容器中保存。Manager类将管理所有Session的生命周期,Session过期将被回收,服务器关闭,Session将被序列化到磁盘等,只要这个HttpSession
对象存在,用户就可以根据Session ID来获取这个对象,也就做到了对状态的保持。
一个SessionID 对应一个访问的客户端,所以一个HttpSession对象也就一个访问的客户端