PHP Cookie 与 Session:状态管理完全指南

小飞兽 PHP 20 次阅读 2026-07-16

Cookie vs Session 区别

两者都是用来在无状态的 HTTP 协议上保存状态的机制。

    • Cookie:数据存储在浏览器端,大小限制约 4KB,可长期存储
    • Session:数据存储在服务器端,浏览器只保存一个 Session ID,大小无限制

    一、Cookie 操作

    设置 Cookie

    <?php\n// 基本设置\nsetcookie('username', '张三');\n\n// 完整参数\nsetcookie('token', 'abc123xyz', [\n    'expires'  => time() + 86400 * 7,    // 7天后过期\n    'path'     => '/',                   // 整站有效\n    'domain'   => '.example.com',        // 域名(带点表示所有子域)\n    'secure'   => true,                  // 仅 HTTPS 传输\n    'httponly' => true,                  // 禁止 JS 读取(防 XSS)\n    'samesite' => 'Strict',              // CSRF 防护:Strict/Lax/None\n]);\n?>

    读取 Cookie

    <?php\n// 在 $_COOKIE 超全局变量中读取\n$username = $_COOKIE['username'] ?? '';\n$token = $_COOKIE['token'] ?? '';\n\n// 注意:Cookie 第一次设置后,需要刷新页面才能在 $_COOKIE 中读取\n?>

    删除 Cookie

    <?php\n// 把过期时间设为过去的时间即可删除\nsetcookie('username', '', time() - 3600, '/');\nsetcookie('token', '', time() - 1, '/');\n?>

    Cookie 常用场景

    <?php\n// 记住用户名\n$remember_user = $_COOKIE['remember_user'] ?? '';\nif ($remember_user) {\n    echo "上次登录的用户名:{$remember_user}";\n}\n\n// 语言偏好\n$lang = $_COOKIE['lang'] ?? 'zh-CN';\n\n// 主题偏好\n$theme = $_COOKIE['theme'] ?? 'light';\n?>

    二、Session 操作

    开启 Session

    <?php\n// 必须在任何输出之前调用\nsession_start();\n\n// 配置 Session(开启后)\nsession_name('MY_SESSION_ID');\nsession_save_path('/var/lib/php/sessions');\n\n// Session 垃圾回收配置(php.ini)\n// session.gc_maxlifetime = 1440(24分钟无活动则过期)\n// session.gc_probability = 1\n// session.gc_divisor = 100\n?>

    读写 Session

    <?php\nsession_start();\n\n// 存储数据\n$_SESSION['user_id'] = 1;\n$_SESSION['username'] = '张三';\n$_SESSION['role'] = 'admin';\n$_SESSION['login_time'] = time();\n\n// 读取数据\necho $_SESSION['username'];\n\n// 检查登录状态\nif (isset($_SESSION['user_id'])) {\n    echo "欢迎,用户 #{$_SESSION['user_id']}";\n} else {\n    echo "请先登录";\n}\n\n// 删除单个键\nunset($_SESSION['login_time']);\n\n// 销毁所有会话(登出)\nsession_destroy();\n?>

    Session 配置详解

    <?php\nsession_start([\n    'cookie_lifetime' => 0,         // 0=浏览器关闭失效,其他数字为指定秒数\n    'cookie_path'     => '/',\n    'cookie_domain'   => '',\n    'cookie_secure'   => isset($_SERVER['HTTPS']),  // HTTPS 自动开启\n    'cookie_httponly' => true,      // 禁止 JS 读取 Cookie\n    'cookie_samesite' => 'Strict',  // CSRF 防护\n    'name'            => 'SESSION_ID', // Session Cookie 名称\n    'gc_probability'  => 1,\n    'gc_divisor'      => 100,\n    'gc_maxlifetime'  => 1800,      // 30分钟无活动过期\n]);\n?>

    三、实战:完整的登录/登出流程

    <?php\n// login.php - 处理登录\nsession_start();\n\nif ($_SERVER['REQUEST_METHOD'] === 'POST') {\n    $username = $_POST['username'] ?? '';\n    $password = $_POST['password'] ?? '';\n\n    // 验证用户(这里简化,实际从数据库查)\n    if ($username === 'admin' && password_verify($password, '$2y$10$...')) {\n        // 登录成功\n        session_regenerate_id(true);  // 防止 Session Fixation 攻击\n        $_SESSION['user_id'] = 1;\n        $_SESSION['username'] = $username;\n        $_SESSION['login_time'] = time();\n\n        // 记住我(选填)\n        if (!empty($_POST['remember'])) {\n            $token = bin2hex(random_bytes(32));\n            // 保存 token 到数据库\n            setcookie('remember_token', $token, time() + 86400 * 30, '/', '', true, true);\n        }\n\n        header('Location: dashboard.php');\n        exit;\n    } else {\n        $error = '用户名或密码错误';\n    }\n}\n?>
    <?php\n// auth.php - 需要登录才能访问的页面\nsession_start();\n\nif (empty($_SESSION['user_id'])) {\n    header('Location: login.php');\n    exit;\n}\n\necho "欢迎,{$_SESSION['username']}";\n?>
    <?php\n// logout.php - 登出\nsession_start();\n\n// 清除 Session\n$_SESSION = [];\n\n// 清除 Session Cookie\nif (ini_get('session.use_cookies')) {\n    $params = session_get_cookie_params();\n    setcookie(\n        session_name(),\n        '',\n        time() - 42000,\n        $params['path'],\n        $params['domain'],\n        $params['secure'],\n        $params['httponly']\n    );\n}\n\nsession_destroy();\nheader('Location: login.php');\nexit;\n?>

    四、Session 存储驱动

    <?php\n// 默认:文件存储(php.ini: session.save_handler = files)\n// 存储在系统临时目录(如 /tmp)\n\n// Redis 存储(生产环境推荐)\nsession_save_path('tcp://127.0.0.1:6379?prefix=PHP SESSION:');\n\n// 数据库存储\n// session_set_save_handler() 自定义存储逻辑\nclass DatabaseSessionHandler {\n    private $pdo;\n\n    public function open($path, $name): bool { ... }\n    public function read($id): string { ... }\n    public function write($id, $data): bool { ... }\n    public function destroy($id): bool { ... }\n    public function gc($maxlifetime): bool { ... }\n}\n\n$handler = new DatabaseSessionHandler($pdo);\nsession_set_save_handler($handler);\nsession_start();\n?>

    五、安全最佳实践

    • Session Fixation:登录后执行 session_regenerate_id(true)
    • XSS 防护:设置 httponly=true,禁止 JS 读取 Cookie
    • CSRF 防护:用 samesite=Strict/Lax + 表单 Token
    • HTTPS:生产环境必须开启 HTTPS,设置 secure=true
    • Session 过期:设置合理的 gc_maxlifetime
    • 敏感操作:重新验证密码后再执行敏感操作
    <?php\n// 完整的 CSRF Token 验证\nsession_start();\n\n// 生成 Token\nif (empty($_SESSION['csrf_token'])) {\n    $_SESSION['csrf_token'] = bin2hex(random_bytes(32));\n}\n\n// 验证 Token(POST 请求)\nif ($_SERVER['REQUEST_METHOD'] === 'POST') {\n    $token = $_POST['csrf_token'] ?? '';\n    if (!hash_equals($_SESSION['csrf_token'], $token)) {\n        die('CSRF 验证失败');\n    }\n}\n\n// 表单中输出 Token\n// echo '<input type="hidden" name="csrf_token" value="' . $_SESSION['csrf_token'] . '">';\n?>

    总结

    • Cookie 存客户端(4KB限制),Session 存服务器端(无限制)
    • 登录状态用 Session,记住用户名用 Cookie
    • 登录后必须 session_regenerate_id(true) 防 Session Fixation
  • 敏感 Cookie 必须设置 httponly + secure + samesite
  • Session 过期时间要合理设置,防止被长时间利用