Session

HTTP 无状态性的核心问题

HTTP 协议本身 不会记住用户上一次的请求,每个请求都是独立的。例如:

  • 问题:用户登录后,跳转到其他页面时,服务器无法自动识别这是同一个用户。
  • Cookie 的局限性:虽然 Cookie 可以存储用户信息,但直接存储敏感数据在客户端(如用户ID)存在安全隐患,且可能被篡改。
  • 解决方案:开发者提出将敏感数据存储在服务器端,客户端仅保存一个随机生成的 ID(Session ID),通过这个 ID 关联服务器数据,解决了安全性和状态管理问题。

Session工作原理流程

  • 客户端访问网站
  • 服务器创建 SessionID
  • 通过 Cookie 发送给浏览器( Set-Cookie 字段)
  • 浏览器后续请求携带 SessionID
  • 服务器匹配 Session 数据
  • 读取/修改数据

流程解析(以 PHP 为例)

用户首次访问

  1. 请求到达服务器:用户访问网站(如 example.com)。
  2. 生成 Session ID:服务器检查请求中是否携带 Session ID。如果是新用户,服务器生成一个 唯一 Session ID(如 abc123),通常通过加密算法(如 PHP 默认使用 sha1)生成。
  3. 创建存储文件:服务器在指定目录(如 /tmp/)生成文件 sess_abc123(注意该文件无后缀名),用于存储该用户的 Session 数据(如用户名、购物车内容等)。
  4. 返回 Session ID:服务器通过 HTTP 响应头 将 Session ID 传递给浏览器。默认使用 Cookie(如 PHPSESSID=abc123),但也可通过 URL 参数传递(不推荐)。

用户后续请求

  1. 携带 Session ID:浏览器在后续请求中自动通过 Cookie 发送 Session ID (PHPSESSID=abc123)。
  2. 服务器读取数据:服务器收到请求后,通过 Session ID 找到对应的文件 sess_abc123,并将文件内容反序列化为 $_SESSION 数组。
    注:序列化 (Serialization):把程序中的对象(如变量、数据结构)转换为标准化格式(字符串/二进制流);反序列化 (Deserialization):将标准化格式的数据重新转换为程序可用的原始对象,恢复其结构和功能。
  3. 数据更新与保存:PHP 脚本执行过程中修改 $_SESSION 的数据,脚本结束时,服务器将 $_SESSION 的数据序列化后写回文件。

一些细节

Session 的创建

Session 的创建和管理是由后端编程语言或框架(应用服务器)实现,如:

  • Tomcat:Java Servlet 容器,运行 Java Web 应用。
  • uWSGI:Python 应用服务器,配合 Django/Flask 使用。
  • Node.js:本身不是应用服务器,但 Express、Koa 等框架提供类似功能。
  • PHP-FPM:专为 PHP 设计的进程管理器,与 Nginx/Apache 配合使用。

服务器端数据存储

  • 默认存储方式:文件存储(每个 Session ID 对应一个文件,如 sess_abc123)。
  • 存储路径:通过 php.inisession.save_path 配置(如 /tmp/)。
  • 序列化格式:数据以键值对形式序列化存储(如 username|s:4:"Lucy";)。
  • 其他存储方式:可自定义存储到数据库、Redis 等。

Session 的生命周期

Session 数据在会话期间(持续操作 Session 时)持续有效,也可通过 session_destroy() 或配置的过期时间( php.ini 中的 session.gc_maxlifetime 配置项)自动清理。若用户在一定时间内没有对服务器进行任何与该 Session 相关的操作(如关闭浏览器),服务器就会认为会话已结束,从而使 Session 失效。
有些应用可能会采用持久化 Session 的方式,将 Session 数据存储在数据库或其他持久化存储介质中。这样即使关闭浏览器,下次打开浏览器访问该网站时,只要在一定的有效期内,仍然可以恢复之前的 Session 状态。(“记住我”功能)

PHP 中的 Session

$_SESSION 数组

与其他超全局变量(如 $_GET$_POST$_COOKIE)不同,$_SESSION 的内容并非直接来自客户端的输入,而是存储在服务器端

当用户首次访问时,PHP 会生成一个唯一的 Session ID,并发送到客户端。后续请求中,客户端携带 Session ID(如通过 Cookie 或 URL),服务器根据该 ID 找到对应的 Session 数据并加载到 $_SESSION 数组中。

Session 工作流程

初始化会话

1
session_start(); // 开启会话,读取或创建 Session ID

当调用 session_start() 时,PHP 会执行以下操作:

  • 检查客户端是否携带有效的 Session ID
    • 如果客户端通过 Cookie 或 URL 传递了有效的 Session ID( Cookie 中默认为 PHPSESSID),PHP 会尝试读取对应的 Session 文件。
    • 如果未找到有效的 Session ID,PHP 会生成一个新的 Session ID,并创建一个新的 Session 文件(通常为空文件,路径由 session.save_path 配置决定)。新的 Session ID 会通过 HTTP 响应头( Cookie 或 URL )将 Session ID 传递给浏览器。
  • **加载数据到 $_SESSION**:
    • 如果 Session 文件中有数据,PHP 会将其反序列化并加载到 $_SESSION 数组中。
    • 如果是一个新会话,$_SESSION 数组初始化为空。

操作 Session 数据

1
2
$_SESSION['username'] = 'Alice'; // 写入数据
echo $_SESSION['username']; // 读取数据
  • **修改 $_SESSION**:
    • 开发者可以在脚本中直接操作 $_SESSION 数组(如 $_SESSION['key'] = 'value')。
    • 此时所有修改仅保存在内存中的 $_SESSION 数组里,尚未写入 Session 文件
  • 自动提交
    • PHP 会在脚本执行结束时(或调用 session_write_close() 时),将 $_SESSION 数组的内容序列化,并写入到 Session 文件中。(默认情况下,Session 文件会被加锁。如果脚本执行时间过长,其他请求访问同一 Session 会被阻塞。可通过 session_write_close() 提前释放锁。)
    • 写入完成后,Session 文件会更新为最新的数据。

**销毁 Session **

1
session_destroy(); // 删除服务器端 Session 数据

可通过 session_destroy() 或配置的过期时间( session.gc_maxlifetime 配置项)来销毁 Session 。

PHP Session 文件的储存路径

1
2
3
4
/var/lib/php/sess_PHPSESSID
/var/lib/php/sessions/sess_PHPSESSID
/tmp/sess_PHPSESSID
/tmp/sessions/sess_PHPSESSID

Session 的常用配置项

  • session.save_path:Session文件存储路径(默认 /tmp)。
  • session.name:Session Cookie的名称(默认 PHPSESSID)。
  • session.cookie_lifetime:Session ID的存活时间(秒,0表示浏览器关闭后失效)。
  • session.gc_maxlifetime:Session数据过期时间(默认1440秒,24分钟)。