Kodokon kodokon.com

会话与 Cookie:登录和购物车

在页面之间记住你的访客:安全的登录、基于会话的购物车,以及得到妥善保护的 Cookie。

10 分钟 · 3 题

在 Kodokon 中打开本课

HTTP 什么都不记得:每个请求都是全新到达的,和前一个请求没有任何关联。会话解决了这个问题:session_start() 会把访客和一个唯一标识符关联起来(该标识符通过 Cookie 传输),并关联一块位于服务器端、可通过 $_SESSION 访问的存储空间。请在每个相关页面的最顶部、任何输出之前调用 session_start()

PHP
<?php

declare(strict_types=1);

session_start();

$storedHash = password_hash('secret123', PASSWORD_DEFAULT);
$password = $_POST['password'] ?? '';

if (password_verify($password, $storedHash)) {
    session_regenerate_id(true);
    $_SESSION['user_id'] = 42;
    header('Location: /account.php');
    exit;
}

echo "Invalid credentials.";
login.php:在实际应用中,哈希值来自你的数据库。

保护一个页面归结为检查用户是否存在于会话中,否则就进行重定向。会话也是购物车的理想临时存储:一个 商品 id => 数量 的关联数组能在页面之间一直保留。

PHP
<?php

declare(strict_types=1);

session_start();

if (!isset($_SESSION['user_id'])) {
    header('Location: /login.php');
    exit;
}

$_SESSION['cart'] ??= [];

$productId = filter_input(
    INPUT_POST,
    'product_id',
    FILTER_VALIDATE_INT
);

if (is_int($productId)) {
    $qty = $_SESSION['cart'][$productId] ?? 0;
    $_SESSION['cart'][$productId] = $qty + 1;
}

$count = array_sum($_SESSION['cart']);
echo "Cart: " . $count . " item(s)";
cart.php:一个受保护的页面,以及一个存储在会话中的购物车。

Cookie 则不同,它存在于用户的设备上,并且在关闭浏览器后依然存在:非常适合保存主题或语言偏好。自 PHP 7.3 起,setcookie() 接受一个选项数组:请务必设置 secure(仅限 HTTPS)、httponly(对 JavaScript 不可见)和 samesite(限制从其他站点发送)。

PHP
<?php

setcookie('theme', 'dark', [
    'expires' => time() + 60 * 60 * 24 * 30,
    'path' => '/',
    'secure' => true,
    'httponly' => true,
    'samesite' => 'Lax',
]);

$theme = $_COOKIE['theme'] ?? 'light';
echo "Active theme: " . $theme;
一个有效期三十天、带有正确标志的偏好设置 Cookie。

退出登录必须和登录一样谨慎地完成:清空 $_SESSION,在服务器端销毁会话,并让承载标识符的 Cookie 过期。

PHP
<?php

session_start();

$_SESSION = [];
session_destroy();

setcookie(session_name(), '', time() - 3600, '/');

header('Location: /login.php');
exit;
logout.php:一次彻底的退出登录,不留任何痕迹。

知识检测

确认你已牢记本课的重点内容。

  1. PHP 会话的数据存储在哪里?
    • 存储在用户设备上的一个 Cookie 里
    • 存储在服务器上;浏览器只在一个 Cookie 里保留一个标识符
    • 存储在每个页面的 URL 中
    • 存储在浏览器的 localStorage 中
  2. 为什么要在成功登录之后立刻调用 session_regenerate_id(true)?
    • 为了防止会话固定攻击:一个在登录前就已知的标识符会变得不可用
    • 为了延长会话 Cookie 的有效期
    • 为了清除旧的购物车数据
  3. Cookie 的 httponly 选项有什么作用?
    • 它会加密 Cookie 的内容
    • 它把 Cookie 限制在不安全的连接上
    • 它让页面的 JavaScript 无法访问该 Cookie,从而限制通过 XSS 进行的窃取