一、为什么需要 Session?(Cookie 的两个问题)
Cookie 虽然解决了 HTTP 无状态的问题,但带来了新问题:
- 存储空间浪费:访问网站越多,浏览器本机保存的 Cookie 越多(每个网站可能下发几十个),占用磁盘/内存。
- 通信效率降低:每次请求都要携带所有 Cookie(可能几百字节甚至几 KB),增加 HTTP 传输开销,拖慢速度。
核心思路 :将大量的键值对信息从客户端转移到服务端存储,客户端只保存一个唯一的标识(类似医院的"诊疗卡号")。
二、Session 是什么?
- Session(会话):服务端为每个客户端开辟的一块存储空间,用于保存该客户端的键值对数据(如用户ID、登录状态、权限等)。
- Session ID:服务端生成的一个唯一标识,通过 Cookie 下发给客户端。客户端后续请求只需携带这个 ID,服务端根据 ID 找到对应的 Session 数据。

关键点: - Session 不是 替代 Cookie,而是基于 Cookie 工作。
- Cookie 中保存的内容被简化为一个 Session ID(如 PHPSESSID=abc123)。
- 真正的用户数据保存在服务端(文件、内存、Redis 等)。
三、Session 的工作流程(四步)
|---------|----------|--------------------------------------------------------|------------------|
| 步骤 | 方向 | 动作 | 说明 |
| 1. 创建会话 | 客户端第一次访问 | 服务端调用 session_start() 生成 Session ID,通过 Set-Cookie 下发 | 此时 Session 存储区为空 |
| 2. 写入数据 | 服务端 | 往 _SESSION 中存入键值对(如 _SESSION['username']='admin' ) | 数据保存在服务端 |
| 3. 验证会话 | 客户端再次访问 | 浏览器自动携带 Cookie 中的 Session ID,服务端根据 ID 找到 Session 数据 | 判断是否已登录、有无权限 |
| 4. 销毁会话 | 用户注销 | 服务端清空 $_SESSION 并调用 session_destroy() ,同时删除相关持久 Cookie | 彻底结束会话 |

四、Session 与 Cookie 的对比
|---------------|-------------------------------|-------------------------------------------|
| 对比项 | Cookie | Session |
| 数据存储位置 | 客户端(浏览器) | 服务端(文件/内存/Redis) |
| 每次请求传输的内容 | 所有 Cookie 的 name=value(可能几十个) | 仅一个 Session ID(如 PHPSESSID=xxx ) |
| 存储大小限制 | 每个域名约 50 个,每个 4KB | 受服务端资源限制,可存大量数据 |
| 安全性 | 明文存储,可被篡改/查看(HttpOnly 可部分防御) | 数据不暴露给客户端,更安全 |
| 生命周期 | 可设置过期时间,也可会话级 | 通常随会话结束或主动销毁 |
| 依赖关系 | 独立存在 | 依赖 Cookie 传递 Session ID(也可通过 URL 重写,但不常用) |
五、Session 的存储位置(配置相关)
- 默认(PHP):以文件形式保存在服务器磁盘上(如 /tmp 或 PHP 配置指定的目录)。每个 Session 对应一个文件,文件名包含 Session ID。 文件内容为序列化后的 $_SESSION 数据,例如:username|s:5:"admin";islogin|i:1;
- 可配置存储方式 :
- 文件(默认)
- 内存缓存(如 Memcached)
- 数据库(如 MySQL)
- Redis(常用,支持分布式)
示例:
修改 PHP 配置 session.save_handler = redis 和 session.save_path = "tcp://127.0.0.1:6379" 可让 Session 存入 Redis。
六、Session 的典型应用场景
- 用户登录状态保持
- 登录成功后,将 user_id 和 is_login 存入 $_SESSION。
- 后续请求只需校验 Session 中是否有 is_login=true,无需重复查询数据库。
- 购物车数据
- 未登录时购物车数据可存 Cookie,登录后合并到 Session 中。
- 验证码/短信验证码存储
- 生成验证码时存入 Session,用户提交时对比,防止篡改。
- 权限控制
- 用户登录后,将角色、权限列表存入 Session,避免每次请求都查数据库。
七、"记住我"(7天免登录)的实现方式
Session 本身默认过期时间较短(如 1440 秒)。要实现"7天内自动登录",需要额外设置持久 Cookie,并配合 Session 使用:
php
// 登录时勾选"记住我"
if($_POST['remember']=="yes") {
setcookie("admin", $username, time()+7*24*60*60);
setcookie("code", md5($username.md5($password)), time()+7*24*60*60);
}
- 真正登录凭证存在 _SESSION 中(_SESSION['username'] 和 $_SESSION['islogin'])。
- 持久 Cookie(admin 和 code)用于下次访问时自动重建 Session。
- 典型自动登录流程:用户再次访问时,先检查 Session 是否为空;若为空则校验持久 Cookie,校验通过后重新 session_start() 并写入 $_SESSION。
八、注销时如何完整清除会话
正确的注销代码应做四件事(参考 logout.php):
php
session_start(); // 1. 恢复当前会话
$username = $_SESSION['username']; // 2. 记录用户名用于提示
$_SESSION = array(); // 3. 清空 $_SESSION 数组
session_destroy(); // 4. 删除服务端的 Session 文件
setcookie("admin",'',time()-1); // 5. 删除"记住我"的持久 Cookie
setcookie("code",'',time()-1); // 6. 同上
注意:
-
这里没有删除 PHPSESSID 这个 Cookie(浏览器还会保留一个过期的 Session ID),但服务端 Session 已销毁,该 ID 无效。如需彻底删除客户端 Session ID,可执行
phpsetcookie(session_name(), '', time()-3600, '/')。 -
持久 Cookie(如 admin、code)必须手动删除,否则下次访问时可能被错误地用于自动登录。
九、完整登录流程时序

|----------------|------------|------------------------------------|--------------------------------------------------------------|
| 步骤 | 文件 | 动作 | 关键代码 |
| 1. 用户访问登录页 | login.html | 显示表单,包含用户名、密码、"记住我"复选框 | 无 |
| 2. 提交表单 | login.php | session_start() 开启会话 | session_start(); |
| 3. 验证账号密码 | login.php | 硬编码验证 admin/123456 | if((username=='admin')\&\&(password=='123456')) |
| 4. 写入 Session | login.php | 保存用户名和登录状态 | _SESSION\['username'\]=username; _SESSION\['islogin'\]=1; |
| 5. 设置持久 Cookie | login.php | 如果勾选"记住我",设置 7 天有效 Cookie | setcookie("admin",username,time()+7*24*60*60); |
| 6. 跳转用户中心 | login.php | header('refresh:3;url=index.php'); | |
| 7. 验证登录状态 | index.php | 检查 _SESSION\['username'\] 是否存在 | if(isset(_SESSION['username'])) |
| 8. 显示个人中心 | index.php | 显示欢迎信息和注销链接 | echo $_SESSION['username'].":你好..."; |
| 9. 注销 | logout.php | 清除 Session 和持久 Cookie | session_destroy(); setcookie("admin",'',time()-1); |
十、注意事项
- session_start() 必须在任何输出之前调用(包括 header() 和 echo),否则会报错。
- 不同浏览器的 Session 互不影响,因为 Cookie 不共享。同一浏览器的不同标签页/窗口共享 Session。
- 分布式环境需将 Session 集中存储(如 Redis),不能使用默认的文件存储。
- Session 不是万能的:对于纯 API 服务(无浏览器环境),更多使用 Token(如 JWT)。
十一、一句话总结
Cookie 存在客户端,用来保存数据(如 Session ID)。
Session 存在服务端,用来保存真正的用户数据。
两者配合:服务端通过 Cookie 下发一个 Session ID,客户端下次请求带上它,服务端就能找到对应的 Session 数据。
这份笔记已经包含了原版的所有内容,并融入了你提供的代码示例和补充说明。如果你觉得还有需要微调的地方(比如去掉某些细节或增加流程图文字),请告诉我。
下面附上参考代码
login.php
php
<?php
header("Content-Type:text/html;charset=utf-8");
// 开启会话,产生sessionid
session_start();
if(isset($_POST['login']))
{
$username = trim($_POST['username']);
$password = trim($_POST['password']);
if(($username=='')||($password==''))
{
header('refresh:3;url=login.html');
echo "用户名或密码不能为空,3秒后跳转到登录页面";
exit;
}
// 用户名和密码没有查表,直接写死,只是个参考
else if(($username!='admin')||($password!='123456'))
{
//用户名或密码错误
header('refresh:3;url=login.html');
echo "用户名或密码错误,3秒后跳转到登录页面";
exit;
}
// 没有查表,直接写死
else if(($username=='admin')&&($password=='123456'))
{
//登录成功将信息保存到session中
$_SESSION['username']=$username;
$_SESSION['islogin']=1;
//如果勾选7天内自动保存,则将其保存到cookie
if($_POST['remember']=="yes")
{
setcookie("admin",$username,time()+7*24*60*60);
setcookie("code",md5($username.md5($password)),time()+7*24*60*60);
}
else
{
setcookie("admin",'',time()-1);
setcookie("code",'',time()-1);
}
//跳转到用户首页
header('refresh:3;url=index.php');
}
}
?>
index.php
php
<?php
header("Content-Type:text/html;charset=utf-8");
session_start();
// session中有这个用户名,代表已经登录
if(isset($_SESSION['username']))
{
echo $_SESSION['username'].":你好,欢迎进入个人中心!<br/>";
echo "<a href='logout.php'>注销</a>";
} else {
// 没有这个用户名的session值
echo "你还未登录,请<a href='login.html'>登录</a>";
}
?>
logout.php
php
<?php
header("Content-Type:text/html;charset=utf-8");
session_start();
//清除session
$username=$_SESSION['username'];
$_SESSION=array();
session_destroy();
//清除cookie
setcookie("username",'',time()-1);
setcookie("code",'',time()-1);
echo "拜拜, $username, 欢迎下次光临";
echo "重新<a href='login.html'>登录</a>";
?>