HTTP 无状态性的核心问题
HTTP 协议本身 不会记住用户上一次的请求,每个请求都是独立的。例如:
- 问题:用户登录后,跳转到其他页面时,服务器无法自动识别这是同一个用户。
- Cookie 的局限性:虽然 Cookie 可以存储用户信息,但直接存储敏感数据在客户端(如用户ID)存在安全隐患,且可能被篡改。
- 解决方案:开发者提出将敏感数据存储在服务器端,客户端仅保存一个随机生成的 ID(Session ID),通过这个 ID 关联服务器数据,解决了安全性和状态管理问题。
Session工作原理流程
- 客户端访问网站
- 服务器创建 SessionID
- 通过 Cookie 发送给浏览器( Set-Cookie 字段)
- 浏览器后续请求携带 SessionID
- 服务器匹配 Session 数据
- 读取/修改数据
流程解析(以 PHP 为例)
用户首次访问
- 请求到达服务器:用户访问网站(如
example.com
)。 - 生成 Session ID:服务器检查请求中是否携带 Session ID。如果是新用户,服务器生成一个 唯一 Session ID(如
abc123
),通常通过加密算法(如 PHP 默认使用sha1
)生成。 - 创建存储文件:服务器在指定目录(如
/tmp/
)生成文件sess_abc123
(注意该文件无后缀名),用于存储该用户的 Session 数据(如用户名、购物车内容等)。 - 返回 Session ID:服务器通过 HTTP 响应头 将 Session ID 传递给浏览器。默认使用 Cookie(如
PHPSESSID=abc123
),但也可通过 URL 参数传递(不推荐)。
用户后续请求
- 携带 Session ID:浏览器在后续请求中自动通过 Cookie 发送 Session ID (
PHPSESSID=abc123
)。 - 服务器读取数据:服务器收到请求后,通过 Session ID 找到对应的文件
sess_abc123
,并将文件内容反序列化为$_SESSION
数组。
注:序列化 (Serialization):把程序中的对象(如变量、数据结构)转换为标准化格式(字符串/二进制流);反序列化 (Deserialization):将标准化格式的数据重新转换为程序可用的原始对象,恢复其结构和功能。 - 数据更新与保存: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.ini
的session.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 传递给浏览器。
- 如果客户端通过 Cookie 或 URL 传递了有效的 Session ID( Cookie 中默认为
- **加载数据到
$_SESSION
**:- 如果 Session 文件中有数据,PHP 会将其反序列化并加载到
$_SESSION
数组中。 - 如果是一个新会话,
$_SESSION
数组初始化为空。
- 如果 Session 文件中有数据,PHP 会将其反序列化并加载到
操作 Session 数据
1 | $_SESSION['username'] = 'Alice'; // 写入数据 |
- **修改
$_SESSION
**:- 开发者可以在脚本中直接操作
$_SESSION
数组(如$_SESSION['key'] = 'value'
)。 - 此时所有修改仅保存在内存中的
$_SESSION
数组里,尚未写入 Session 文件。
- 开发者可以在脚本中直接操作
- 自动提交:
- PHP 会在脚本执行结束时(或调用
session_write_close()
时),将$_SESSION
数组的内容序列化,并写入到 Session 文件中。(默认情况下,Session 文件会被加锁。如果脚本执行时间过长,其他请求访问同一 Session 会被阻塞。可通过session_write_close()
提前释放锁。) - 写入完成后,Session 文件会更新为最新的数据。
- PHP 会在脚本执行结束时(或调用
**销毁 Session **
1 | session_destroy(); // 删除服务器端 Session 数据 |
可通过 session_destroy()
或配置的过期时间( session.gc_maxlifetime
配置项)来销毁 Session 。
PHP Session 文件的储存路径
1 | /var/lib/php/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分钟)。