Web中的会话控制
会话控制
- 会话控制
- Cookies
- Cookie如何工作
- Cookie的用途
- Cookie关键属性
- Session
- 会话控制的工作原理(最常见方式:Cookie + Session Storage)
会话控制
因为互联网当初设计的时候,为了考虑简单,Http协议是无状态的协议,什么是无状态呢?也就是计算机每一次对话,他都认为是新的,不会记住之前的状态。这就导致了,如果我们在Java Web程序中,登录了账号,下一次重新发起请求,服务器并不会认为新发起的请求是哪个用户发起的,这就需要每一次发起请求的时候都需要重新登陆,这样用户肯定是不接受的。
会话控制的目的
为了解决上面所述的HTTP无状态导致的问题,比如需要保持用户登录的状态,跟踪用户行为,存储用户的偏好,我们引入了会话控制。其核心目的就是为了指定用户在一段时间内多次的交互建立一个有状态的上下文。
Cookies
既然Http协议是无状态的,我们又需要识别出之前的用户,如何解决这个问题?你可以想象一个这种场景,我们是一个大型商场。商场外边有一队自行车,为了防止自行车被盗,管理人员需要识别每一辆自行车以及自行车的主人,但是自行车太多了,人也太多了,管理人员不可能通过记忆记清楚每一辆自行车与其归属(类似Http无状态)。怎么办呢? 我们可以在每一辆自行车入库的时候,发给车主一个手牌,下次车主来的时候,提供出手牌,我们就给他对应的自行车。OK,对应到Http协议中的解决方案,就是通过请求到来的时候,服务器设置一个Cookie给客户端,之后每次客户端请求的时候都要携带这个Cookie,这样服务端就可以通过识别Cookie来识别用户。
Cookie如何工作
- 首次访问的时候,服务器在发送响应的时候,会在HTTP响应头中包含一个Set-Cookie值,这个值告诉客户端,保存这个值,并在下次访问服务器的时候携带这个值;
- 浏览器接收到服务器的响应的时候,会把Cookie按照指定的名称 值 有效期 作用域(域名 路径)等属性存在到设备的特定位置;
- 后续访问的时候,当再次访问同一服务器,浏览器会在发送请求的时候,自动检查Cookie,找到匹配的Cookie的时候,会将Cookie放到请求头中的Cookie字段并发给服务器;
- 服务器收到请求,会解析Cookie头,就能获取到之前服务器存储到Cookie中的信息,这样服务器就可以利用这些信息来识别用户身份,恢复会话,或者提供用户个性化服务。
- 服务器处理完请求之后,可以再次通过Set-Cookie响应头来更新Cookie或者设置新的Cookie
Cookie的用途
-
会话管理: 登录状态、购物车内容、游戏得分等需要在多次请求间保持的数据。
-
个性化: 用户偏好设置(语言、主题、字体大小)、位置信息、自定义内容。
-
追踪: 分析用户行为(页面浏览、点击流)、广告定位(跨站兴趣追踪 - 主要通过第三方 Cookie)。
Cookie关键属性
- Name 和 Value:
必需项。存储的实际数据。名称和值通常是字符串,但值可以进行编码(如 URL 编码)以存储更复杂的数据或包含特殊字符。
- Expires:
指定 Cookie 的绝对过期时间(GMT 格式)。浏览器会在此时间之后自动删除 Cookie。
示例: Expires=Thu, 31 Oct 2024 07:28:00 GMT
- Max-Age:
指定 Cookie 的相对过期时间(以秒为单位)。表示 Cookie 从被设置起多少秒后过期。优先级高于 Expires。
示例: Max-Age=2592000 (30 天)
-
会话 Cookie: 如果 Expires 和 Max-Age 都未设置,则该 Cookie 是会话 Cookie。它仅在当前浏览器窗口或标签页打开期间有效。关闭浏览器窗口,该 Cookie 就会被删除。
-
Domain:
指定 Cookie 有效的主机名。默认情况下,Cookie 只对设置它的主机有效(不包括子域名)。设置了 Domain 后,Cookie 对该域名及其所有子域名都有效。
示例: 如果由 www.example.com 设置 Domain=example.com,则该 Cookie 对 www.example.com、shop.example.com、api.example.com 等都可见。不能设置为顶级域(如 .com)或公共后缀(如 .co.uk)之外的域。
- Path:
指定 Cookie 有效的 URL 路径前缀。只有路径匹配的请求才会携带该 Cookie。默认为设置该 Cookie 的 URL 的路径。
示例: Path=/shop 表示该 Cookie 只会在访问 /shop 及其子路径(如 /shop/cart, /shop/products)时发送,访问 /blog 则不会发送。
- Secure:
标记为 Secure 的 Cookie 只应通过被 HTTPS 协议加密的请求发送给服务器。这有助于防止 Cookie 在传输过程中被窃听。
- HttpOnly:
标记为 HttpOnly 的 Cookie 无法通过客户端的 JavaScript API(如 document.cookie)访问。这是重要的安全措施,主要用于保护包含会话标识符 (Session ID) 的 Cookie 免受跨站脚本 (XSS) 攻击窃取。
- SameSite:
极其重要的安全与隐私属性! 控制 Cookie 是否在跨站点(Cross-Site)请求中发送。主要用于防御跨站请求伪造 (CSRF) 攻击,并限制第三方 Cookie 的追踪。
取值:
-
Strict: 最严格。Cookie 仅在第一方上下文(即当前浏览器地址栏显示的 URL 与 Cookie 的 Domain 匹配)中发送。用户从其他网站链接点击过来(跨站请求)时,Cookie 不会发送。
-
Lax: (现代浏览器默认值,如果未显式设置且未设置旧属性)。在跨站请求中,仅对安全的 HTTP 方法(如 GET)且是顶级导航(用户在地址栏输入 URL、点击链接)时发送 Cookie。对于 POST 提交表单、iframe 加载、img/fetch/XHR 等非导航请求,不会发送 Cookie。提供了 CSRF 防护和一定的用户友好性。
-
None: Cookie 会在所有上下文中发送,包括跨站点请求。必须同时设置 Secure 属性(即仅限 HTTPS)。这是允许第三方 Cookie 继续工作的方式,但正被浏览器逐步淘汰或限制。
示例: SameSite=Lax; Secure (注意,Secure 是 SameSite=None 的强制要求,对于 Lax/Strict 则不是强制但推荐)
Session
上面介绍了很多关于Cookie的知识,但是Cookie是保存在浏览器端的,浏览器每次访问服务器都需要发回Cookie,如果我们把大量信息存储在Cookie中,就会导致网络负载急速上升。那么既然客户端不能存储大量信息,我们又有存储信息的需求,我们应该怎么办呢?是不是我们可以将大量信息存储在服务器端。
答案是肯定的,我们可以在服务器端为每次会话创建一个对象,然后在这个对象中保存相关的信息。但是下一个问题又来了,我们如何将我们创建的对象与实际的会话建立起连接? 实际上 服务器会给我们创建的对象设置一个id,这个id是独一无二的,当请求到来的时候,我们服务端将这个id保存到Cookie中,这样后续的请求自动携带Cookie,服务器再通过Cookie中的id值就可以找到我们创建的对象。
会话控制的工作原理(最常见方式:Cookie + Session Storage)
- 会话启动:
-
用户首次访问网站(或触发需要会话的操作,如登录)。
-
服务器端应用程序(如 PHP, Java Servlet, Node.js/Express, Python/Django/Flask, ASP.NET)检测到没有有效的 Session ID。
-
服务器创建一个新的 Session 对象,为其生成一个唯一的 Session ID,并在服务器端(内存、数据库、缓存)开辟空间存储与该 Session 相关的数据(此时可能为空)。
-
服务器在HTTP 响应头中包含一个 Set-Cookie 指令,将这个 Session ID 发送给用户的浏览器。这个 Cookie 通常命名为类似 JSESSIONID (Java), PHPSESSID (PHP), sessionid (Django) 等。
HTTP/1.1 200 OK
Set-Cookie: JSESSIONID=a3fG7dKj2pQ9wZb1; Path=/; HttpOnly; Secure
...其他响应头和内容...
- 浏览器存储 Session ID:
- 浏览器接收到 Set-Cookie 头,将包含 Session ID 的 Cookie 存储在本地(内存或硬盘,取决于 Cookie 设置)。
- 后续请求:
- 当用户在同一浏览器中访问该网站的任何页面(符合 Cookie 的 Path 和 Domain 规则)时,浏览器会自动在 HTTP 请求头中包含之前存储的 Session ID Cookie。
GET /my-account HTTP/1.1
Host: www.example.com
Cookie: JSESSIONID=a3fG7dKj2pQ9wZb1
...其他请求头...
- 服务器识别与关联:
-
服务器接收到请求,从 Cookie 头中提取出 Session ID。
-
服务器使用这个 Session ID 查找其存储的对应 Session 数据(在内存、数据库或缓存中)。
-
如果找到有效的、未过期的 Session 数据,服务器应用程序就能读取和修改这些数据(例如,获取当前登录的用户ID、购物车内容、用户偏好)。
-
服务器处理请求,生成响应(可能会更新 Session 数据),并将响应发送回浏览器。响应中通常不需要再次设置 Session ID Cookie(除非需要更新或延长其有效期)。
- 会话结束:
-
显式结束: 用户点击“注销”,服务器清除对应的 Session 数据并使 Session ID 失效。通常也会发送一个清除 Cookie 的指令(设置过期时间为过去)。
-
超时结束: 服务器为每个 Session 设置一个空闲超时(如 30 分钟)。如果用户在该时间段内没有任何操作(即没有发送包含 Session ID 的请求),服务器会自动清除 Session 数据,Session 失效。下次用户再访问时,需要重新建立会话(可能需要重新登录)。
-
浏览器关闭结束(可配置): 默认情况下,存储在浏览器内存中的 Cookie(称为 Session Cookie)会在浏览器关闭时清除。这意味着 Session ID 丢失,即使服务器上的 Session 数据可能还未超时,用户也无法再关联回去(下次访问被视为新会话)。如果 Cookie 设置了 Expires 或 Max-Age 属性,它会被持久化到硬盘,浏览器关闭后仍存在,直到过期。服务器端的 Session 数据超时机制仍然是有效的安全措施。