Session 与会话管理
Session 是 Web 开发中保存用户状态的重要机制。本文介绍 PHP Session 的使用方法和安全实践。
基础配置与使用
php
// php.ini 推荐配置
session.cookie_httponly = 1 // 防止 XSS 窃取
session.cookie_secure = 1 // 仅 HTTPS 传输
session.cookie_samesite = "Strict" // CSRF 保护
session.gc_maxlifetime = 3600 // 1小时过期
session.use_strict_mode = 1 // 严格模式
// 代码中启动 session
session_start();
// 存储数据
$_SESSION['user_id'] = 123;
$_SESSION['username'] = '张三';
// 读取数据
$userId = $_SESSION['user_id'] ?? null;
// 删除数据
unset($_SESSION['user_id']);
// 销毁整个 session
session_destroy();安全实践
php
// 1. 重新生成 ID(防止会话固定攻击)
session_start();
if (!isset($_SESSION['initialized'])) {
session_regenerate_id(true); // true = 删除旧 session 文件
$_SESSION['initialized'] = true;
}
// 2. 权限变更时重新生成
function login($userId) {
session_regenerate_id(true);
$_SESSION['user_id'] = $userId;
$_SESSION['login_time'] = time();
}
// 3. 验证会话合法性
function validateSession() {
if (empty($_SESSION['user_id'])) {
return false;
}
// 检查 IP 是否一致(可选,视业务而定)
if ($_SESSION['ip'] !== $_SERVER['REMOTE_ADDR']) {
session_destroy();
return false;
}
// 检查 User-Agent
if ($_SESSION['ua'] !== $_SERVER['HTTP_USER_AGENT']) {
session_destroy();
return false;
}
return true;
}自定义 Session 处理器(Redis)
php
// 使用 Redis 存储 Session
class RedisSessionHandler implements SessionHandlerInterface {
private $redis;
private $ttl = 3600;
public function __construct($redis) {
$this->redis = $redis;
}
public function open($savePath, $sessionName): bool {
return true;
}
public function close(): bool {
return true;
}
public function read($id): string {
return $this->redis->get("session:$id") ?: '';
}
public function write($id, $data): bool {
return $this->redis->setex("session:$id", $this->ttl, $data);
}
public function destroy($id): bool {
return $this->redis->del("session:$id") > 0;
}
public function gc($maxlifetime): int {
return 0; // Redis 自动过期
}
}
// 注册处理器
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);
$handler = new RedisSessionHandler($redis);
session_set_save_handler($handler, true);
session_start();Session 数据序列化问题
php
// 默认序列化方式可能不同系统不兼容
// 统一使用 json 序列化
ini_set('session.serialize_handler', 'json');
// 或者使用 igbinary(性能更好)
ini_set('session.serialize_handler', 'igbinary');
// 处理闪存消息(Flash Message)
function setFlash($key, $message) {
$_SESSION['flash'][$key] = $message;
}
function getFlash($key) {
$message = $_SESSION['flash'][$key] ?? null;
unset($_SESSION['flash'][$key]);
return $message;
}多服务器 Session 共享
php
// 方案1:使用 Redis(推荐)
// 所有服务器共享同一个 Redis
// 方案2:使用数据库存储
class DbSessionHandler implements SessionHandlerInterface {
private $pdo;
public function read($id): string {
$stmt = $this->pdo->prepare("SELECT data FROM sessions WHERE id = ?");
$stmt->execute([$id]);
return $stmt->fetchColumn() ?: '';
}
public function write($id, $data): bool {
$stmt = $this->pdo->prepare("
INSERT INTO sessions (id, data, updated_at)
VALUES (?, ?, NOW())
ON DUPLICATE KEY UPDATE data = ?, updated_at = NOW()
");
return $stmt->execute([$id, $data, $data]);
}
}注意事项
- session_start() 必须在输出之前调用
- 不要在 session 中存储敏感信息(如密码、密钥)
- 定期清理过期 session,使用 Redis 时设置 TTL
- 跨域场景注意 SameSite Cookie 设置
- 高并发下考虑使用 Redis 而非文件存储
php
// 开启缓冲区避免 headers already sent 错误
ob_start();
session_start();
// 或者在 php.ini 配置
output_buffering = On