萬盛學電腦網

您现在的位置: 萬盛學電腦網 >> 程式設計 >  >> 網路程式設計 >> PHP 程式設計

PHP Session 與 ThinkPHP Session 深度整合指南

前言

Session 是有狀態 Web 應用的核心。本文涵蓋原生 PHP Session 儲存優化、分散式方案、ThinkPHP 多版本 Session 封裝用法、私有命名空間、資料庫驅動與自定義驅動開發。

一、原生 Session 儲存優化

1. 檔案儲存的目錄分散 (大流量必備)

; php.ini
; 格式:session.save_path = "N;MODE;/path"
; N = 目錄層級 (1-2 建議),MODE = 目錄權限 (八進制)
session.save_path = "2;600;/var/lib/php/sessions"

效果:建立 36×36=1296 個子目錄 (0-9, a-z),大幅降低單目錄 inode 壓力。

2. 自動建立子目錄腳本

二、分散式 Session 方案

方案一:資料庫儲存 (session_set_save_handler)

pdo = $pdo;
        $this->table = $table;
    }
    
    public function open($path, $name): bool { return true; }
    public function close(): bool { return true; }
    
    public function read($id): string|false {
        $stmt = $this->pdo->prepare("SELECT data FROM {$this->table} WHERE id = ? AND expiry > ?");
        $stmt->execute([$id, time()]);
        return $stmt->fetchColumn() ?: '';
    }
    
    public function write($id, $data): bool {
        $expiry = time() + (int)ini_get('session.gc_maxlifetime');
        $stmt = $this->pdo->prepare(
            "INSERT INTO {$this->table} (id, data, expiry) VALUES (?, ?, ?)
             ON DUPLICATE KEY UPDATE data = VALUES(data), expiry = VALUES(expiry)"
        );
        return $stmt->execute([$id, $data, $expiry]);
    }
    
    public function destroy($id): bool {
        $stmt = $this->pdo->prepare("DELETE FROM {$this->table} WHERE id = ?");
        return $stmt->execute([$id]);
    }
    
    public function gc($maxLifetime): int|false {
        $stmt = $this->pdo->prepare("DELETE FROM {$this->table} WHERE expiry < ?");
        $stmt->execute([time()]);
        return $stmt->rowCount();
    }
}

// 註冊 (必須在 session_start 前)
$handler = new DbSessionHandler($pdo);
session_set_save_handler($handler, true);
session_start();

方案二:Redis 儲存 (最高效能)

; php.ini 全域設定
session.save_handler = redis
session.save_path = "tcp://127.0.0.1:6379?auth=password&database=0&prefix=session:"

; 或多節點
session.save_path = "tcp://host1:6379,tcp://host2:6379?weight=2&timeout=2.5"

方案三:Memcached (簡單叢集)

; php.ini
session.save_handler = memcached
session.save_path = "127.0.0.1:11211"
; 多節點
session.save_path = "host1:11211,host2:11211"

三、ThinkPHP Session 完整用法 (v5.1/v6.0)

1. 門面靜態介面

2. 私有命名空間 (模組隔離)


// 僅在當前模組/應用有效,避免衝突
Session::set('token', 'abc', 'api'); // 命名空間 'api'
$token = Session::get('token', '', 'api');
Session::has('token', 'api');
Session::delete('token', 'api');

3. 配置參數 (config/session.php)

return [
    'type'       => 'file',     // file, redis, memcache, db
    'path'       => '',         // 檔案儲存路徑 (空用系統預設)
    'prefix'     => 'think',    // Session 鍵前綴
    'expire'     => 3600,       // 生命週期 (秒)
    'auto_start' => true,       // 自動啟動
    'name'       => 'PHPSESSID', // Cookie 名稱
    'domain'     => '',         // Cookie 域名
    'secure'     => false,      // HTTPS only
    'httponly'   => true,       // 防 XSS
    'same_site'  => 'Lax',      // CSRF 防護
    'var_session_id' => '',     // URL 參數傳遞 ID 變數名
    
    // Redis 專用
    'host'       => '127.0.0.1',
    'port'       => 6379,
    'password'   => '',
    'select'     => 0,
    'timeout'    => 0,
    'expire'     => 3600,
    
    // DB 專用
    'table'      => 'think_session',
];

4. 資料庫 Session 遷移 (MySQL)

CREATE TABLE `think_session` (
    `id` CHAR(40) NOT NULL PRIMARY KEY,
    `session_id` VARCHAR(255) NOT NULL,
    `session_expires` INT UNSIGNED NOT NULL,
    `session_data` BLOB,
    INDEX `expires` (`session_expires`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

配置 'type' => 'db' 即可啟用。

5. 自定義驅動開發

handler = new \Redis();
        $this->handler->connect($config['host'], $config['port']);
    }
    
    public function open($savePath, $sessionName): bool { return true; }
    public function close(): bool { return true; }
    public function read($sessId): string { 
        return $this->handler->get("session:$sessId") ?: ''; 
    }
    public function write($sessId, $data): bool {
        return $this->handler->setex("session:$sessId", $this->config['expire'], $data);
    }
    public function destroy($sessId): bool {
        return $this->handler->del("session:$sessId");
    }
    public function gc($maxLifetime): int { return 0; } // Redis 自動過期
}

配置 'type' => '\\app\\session\\driver\\CustomDriver' 即可使用。

四、登入驗證實戰範例

post('username');
    $password = $request->post('password');
    
    $user = User::where('username', $username)->find();
    if ($user && password_verify($password, $user->password)) {
        // 寫入 Session
        Session::set('user.id', $user->id);
        Session::set('user.name', $user->username);
        Session::set('user.role', $user->role);
        
        // 重生 ID 防固定攻擊
        Session::regenerate();
        
        return redirect('dashboard');
    }
    return $this->error('帳號或密碼錯誤');
}

// 中介層驗證 (Middleware)
public function handle(Request $request, Closure $next) {
    if (!Session::has('user.id')) {
        return redirect('login')->with('msg', '請先登入');
    }
    return $next($request);
}

// 登出
public function logout() {
    Session::clear();
    Session::destroy();
    return redirect('login');
}

五、Session 安全加固清單

  • [ ] session.cookie_httponly = 1
  • [ ] session.cookie_secure = 1 (HTTPS 環境)
  • [ ] session.cookie_samesite = 'Lax'
  • [ ] session.use_strict_mode = 1
  • [ ] 登入後 Session::regenerate()
  • [ ] 敏感操作二次驗證 (重新輸入密碼/OTP)
  • [ ] 定期審計 Session 儲存介質權限 (檔案 0600、Redis ACL)

總結

Session 管理的演進路徑:檔案 → 分散檔案 → 資料庫 → Redis/Memcached → 自定義驅動。ThinkPHP 提供優雅的門面介面、命名空間隔離、多驅動支援,滿足從單體到微服務的各階段需求。核心原則:儲存介質可替換、業務邏輯不變、安全配置不可少。

copyright © 萬盛學電腦網 all rights reserved