会话技术
1. 会话技术简介
会话的概念:
- from 清华大学郑莉:从一个客户端打开浏览器连接到服务器的某个服务目录,到客户关闭浏览器离开该服务目录称为一个会话,每个会话只对应于一个客户,并且可以跨多个页面。
- from 黑马程序员课程:浏览器第一次给服务器资源发送请求时,会话建立,直到有一方断开(指客户端浏览器关掉或服务器关掉?)为止。一次会话中包含多次请求和响应。
会话技术的功能:在一次会话的多次请求间,共享数据。
客户端跟踪:
由于 HTTP 是无状态协议,服务器不能自动维护客户连接的上下文信息。
而许多情况下,Web 服务器必须要能够跟踪用户的状态,比如购物网站和电子邮件网站,当用户登录以后,其身份和一系列的操作状态都需要被跟踪并保持。
Servlet API 提供了两种可以跟踪客户端状态的方法:Cookie、Session。
类型 解释 Cookie 客户端会话技术:将数据保存到客户端 Session 服务器端会话技术:将数据保存到服务端
2. Cookie
2.1 Cookie 使用步骤
- 创建 Cookie 对象,绑定数据:
new Cookie(String name, String value)
; - 发送 Cookie 对象,即将 Cookie 对象插入到请求中:
response.addCookie(Cooki cookie)
- 获取 Cookie,拿到数据:
Cookie[] request.getCookies()
2.2 Cookie 实现原理
Cookie 通俗地说就是当一个用户通过 HTTP 访问一个服务器时,这个服务器会将一些 Key/Value 键值对返回给客户端浏览器,并给这些数据加上一些限制条件,在条件符合时这个用户下次访问这个服务器时,数据又被完整地带回给服务器。
当初 W3C 在设计 Cookie 时实际上考虑的是为了记录用户在一段时间内访问 Web 应用的行为路径。由于 HTTP 是一种无状态协议,当用户的一次访问请求结束后,后端服务器就无法知道下一次来访问的还是不是上次访问的用户。
Tomcat 创建 Set-Cookie
响应头时的时序图:

由上图可知,真正构建 Cookie 是在org.apache.catalina.connector.Response
类中完成的,调用generateCookieString
方法将 Cookie 对象构造成一个字符串,构造的字符串的格式如userName="junshan";Version="1";Domain="xulingbo.net";Max-Age=1000
。然后将这个字符串命名为 Set-Cookie
添加到 MimeHeaders
中。
在这里有以下几点需要注意:
- 所创建 Cookie 的 NAME 不能和 Set-Cookie 或者 Set-Cookie2 的属性项值一样,否则会抛出
IllegalArgumentException
异常; - 所创建 Cookie 的 NAME 和 VALUE 的值不能设置成非 ASSIC 字符,如果要使用中文,可以通过 URLEncoder 将其编码,否则会抛出
IllegalArgumentException
异常; - 当 NAME 和 VALUE 的值出现一些 TOKEN 字符(如“\”、“,”等)时,构建返回头会将该 Cookie 的 Version 自动设置为 1;
- 当在该 Cookie 的属性项中出现 Version 为 1 的属性值时,构建 HTTP 响应头同样会将 Version 设置为 1。
当我们通过 response.addCookie 创建多个 Cookie 时,这些 Cookie 都以独立的 Header 存在,也就是说每一次的创建都会创建一个以 NAME 为 Set-Cookie 的 MimeHeaders。
当我们请求某个 URL 路径时,浏览器会根据这个 URL 路径将符合条件的 Cookie 放在 Request 请求头中传回给服务端,服务端通过 request.getCookies() 来取得所有 Cookie。
目前很多工具都可以观察甚至修改浏览器中的 Cookie 数据。
Cookie 是 HTTP 头中的一个字段,虽然 HTTP 本身对这个字段并没有多少限制,但毕竟 Cookie 是存储在浏览器里,所以不同的浏览器对 Cookie 的存储都有一些限制。
Cookie 的压缩——暂略。
以下来自黑马程序员视频:
Cookie 基于响应头
set-cookie
和请求头cookie
实现。
2.3 Cookie 数据保存时长
Cookie 数据在浏览器中保存的时间:
默认情况下,当浏览器关闭后,Cookie 数据即被销毁,因为数据存储在浏览器内存中。
Cookie 也可持久化存储:通过 Cookie 对象的方法
setMaxAge(int seconds)
seconds
作用 正数 将 Cookie 数据写到硬盘的文件中, seconds
表示这个 cookie 文件的存话时间负数 默认值 零 选择删除 Cookie 信息,在内存/硬盘中都不再有 cookie 数据
2.4 Cookie 数据共享
对于同一个 tomcat 服务器:
- 默认情况下,如果在一个 tomcat 服务器中部署了多个 web 项目,这些 web 项目中的 cookie 不能共享;
- 通过 Cookie 对象的 setPath(String path) 方法,可以设置 cookie 的获取范围(默认情况下其设置当前的虚拟目录);
- 如果要共享,可选择将上述 path 的范围扩大,如设置为"/"。
对于不同的 tomcat 服务器:
不同的服务器之间的数据当然不可能直接被共享;
可通过设置 Cookie 对象的 setDomain 方法进行共享:
用法 作用 setDomain(String path)
如果设置一级域名相同,那么多个服务器之间 cookie 可以共享 setDomain(".baidu.com")
那么 tieba.baidu.com 和 news.baidu.com 中的 cookie 即可共享
2.5 Cookie 的特点
- cookie 存储数据在客户端浏览器,可能不太安全;
- 浏览器对单个 cookie 的大小有限制(一般为 4kb),以及对同一个域名下的总 cookie 数量也有限制(一般为 20 个)
2.6 Cookie 的作用
- Cookie 一般用于存储少量的不敏感数据;
- 在不登录账户的情况下,完成服务器对客户端的身份识别。
2.7 其他注意事项
- 一次可以发送多个 cookie,只要创建多个 Cookie 对象,然后多次使用 response 调用 addCookie 方法发送 cookie 即可(一个 cookie 相当于一个键值对,多个 cookie 相当于多个键值对?)。
- 在 Tomcat 8 之前,cookie 不能直接存储中文数据,需要将中文数据转码,一般采用 URL 编码。
- Cookie 案例 https://www.bilibili.com/video/BV1uJ411k7wy?p=746 。
3. Session
Session 是服务器端会话技术,在一次会话的多次请求间共享数据,将数据保存在服务器端的 HttpSession 对象中。
和 Request、ServletContext 对象一样,Session 对象也是一个域对象。
Session 对象的获取:HttpSession session = request.getSession();
在一次会话范围内,多次获取的 Session 对象始终是同一个对象。
3.1 使用 HttpSession 对象
方法 | 作用 |
---|---|
void setAttribute(String name, Object value) | 存储数据 |
Object getAttribute(String name) | 获取数据 |
void removeAttribute(String name) | 删除数据 |
3.2 Session 的实现原理
Session 的实现是依赖于 Cookie 的(实际上有三种工作方式):

同一个客户端每次和服务端交互时,不需要每次都传回所有的 Cookie 值,而是只要传回一个 ID,这个 ID 是客户端第一次访问服务器时生成的,而且每个客户端是唯一的。这样每个客户端就有了一个唯一的 ID,客户端只要传回这个 ID 就行了,这个 ID 通常是 NAME 为 JSESSIONID
的一个 Cookie。一旦你关闭了浏览器端的 Cookie 功能,那么 Cookie 与 Session 功能都将不支持。
Session ID:
- Servlet 容器为 HttpSession 分配的一个唯一标识符;
- Session ID 将作为 Cookie 保存在客户的浏览器中。
有了 Session ID,服务端就可以创建HttpSession
对象了,第一次触发通过request.getSession()
方法。如果当前的 Session ID 还没有对应的HttpSession
对象,那么就创建一个新的,并将这个对象加到org.apache.catalina.Manager
的 sessions 容器中保存。Manager 类将管理所有 Session 的生命周期,Session 过期将被回收,服务器关闭,Session 将被序列化到磁盘等。只要这个 HttpSession 对象存在,用户就可以根据 SessionID 来获取这个对象,也就做到了对状态的保持:


Session Cookie:
- 每次客户端发出 HTTP 请求时,Servlet 容器可以从 HttpRequest 对象中读取 SessionID,然后根据 Session ID 在服务器端找到相应的 HttpSession 对象,从而获取客户的状态信息,这样的 Cookie 叫做 Session Cookie。
- 对于浏览器而言,其存储于浏览器内存中,并不写到磁盘上。
- 其针对某一次会话而言,会话结束后也就随之消失了。
3.3 Session 对象的销毁
Session 对象的销毁时机:
- 服务器关闭;
- Session 对象调用 invalidate() 方法;
- Session 对象的默认失效时间为 30 分钟,但也可通过修改配置文件进行更改。
3.4 Session 的特点
相较 Cookie 而言,Session 的安全性要高很多,因为 Session 是将数据保存在服务端,只是通过 Cookie 传递一个 SessionID 而已,所以 Session 更适合存储用户隐私和重要的数据。
- 用于存储一次会话的多次请求的数据,存储在服务器端;
- 可以存储任意类型、任意大小的数据(Cookie 饼干,Session 主菜)。
3.5 多终端 Session 统一
当前大部分网站都有了无线端,对无线端的 Cookie 如何处理也是很多程序员必须考虑的问题。
问题:
- 一种情况,在无线端可能会通过手机访问无线服务端系统,同时也会访问 PC 端的服务系统,如果它们两个的登录系统没有统一的话,将会非常麻烦,可能会出现二次登录的情况;
- 另一种情况,在手机上登录以后再在 PC 上同样访问服务端数据,Session 能否共享就决定了客户端是否要再次登录。
解决方案:
多端共享 Session:
多终端登录:
3.6 其他细节
默认情况下,当客户端关闭后、服务器不关闭,两次获取的 Session 对象并非同一个。
如果需要相同,可以创建 Cookie,键为
JSESSIONID
,设置最大存活时间,让 cookie 在浏览器中持久化保存。Cookie c = new Cookie("JSESSIONID", session.getId()); c.setMaxAge(60*60); // 1 小时 response.addCookie(c);
默认情况下,当客户端不关闭、服务器关闭后,两次获取的 Session 对象不是同一个。
有时候需要确保这样的 Session 对象虽然不一样,但是数据不丢失:
session 的钝化 session 的活化 在服务器正常关闭之前,将 Session 对象序列化到硬盘中 在服务器启动后,将 Session 文件转化为内存中的 Session 对象
4. 浏览器不支持 Cookie 与 Session 时:URL 重写技术
浏览器不支持 Cookie 与 Session 的情况下,可以使用 URL 重写技术来实现会话管理。
- 向 URL 连接添加参数,并把 Session ID 作为值包含在连接中。
- 为 Servlet 响应部分的每个连接添加 Session ID,可以使用方法对:
response.encodeURL()
:使 URL 包含 Session IDresponse.encodeRedirectURL()
:使用重定向
5. 表单重复提交问题
要访问表单重复提交,就要标识用户的每一次访问请求,使得每一次访问对服务端来说都是唯一确定的。为了标识用户的每次访问请求,可以在用户请求一个表单域时增加一个隐藏表单项,这个表单项的值每次都是唯一的 token,如:
<form id="form" method="post">
<input type=hidden name="crsf_token" value="xxx" />
</form>
当用户在请求时生成这个唯一的 token 时,同时将这个 token 保存在用户的 Session 中,等用户提交请求时检查这个 token 和当前的 Session 中保存的 token 是否一致。如果一致,则说明没有重复提交,否则用户提交上来的 token 已经不是当前这个请求的合法 token。其工程过程如图:

验证表单的过程如下图:
